Introduction

This coursework focuses on housing prices, with the main objective being to predict the price of a property based on various inputs. The inputs include features such as the area, the number and types of rooms, and additional factors like the availability of a main road, hot water heating, and more.

The dependent variable is the price, as it is the primary concern for most people searching for a house. The goal of this work is to predict the price based on diverse inputs, which consist of mixed data types, such as:

  • Numerical values
  • Text-based responses like “yes” or “no”
  • Categories for furnishing status, including “furnished,” “semi-furnished,” or “non-furnished.”

This project addresses a regression problem because the objective is to predict a numeric value—in this case, the price of the property.

Collection / Preparation

Now we are going to import our dataset into this project.

dt_houses <- fread(file = "./datasets/Regression_set.csv")


I would like to check, if i have some nullish data in my dataset. I think it is a good idea to go through all rows and colums and check, if there is a NA. I want to check it with built-in function in R complete.cases(data_table). This function returns TRUE or FALSE if row contains a NA value.

nas <- dt_houses[!complete.cases(dt_houses)]
nas

That looks great, now we can explore our dataset :)

Exploration

Before we will explore our data, I want to import all libraries, which we will probably use:

library(data.table)
library(ggcorrplot)
library(ggExtra)
library(ggplot2)
library(ggridges)
library(ggsci)
library(ggthemes)
library(RColorBrewer)
library(svglite)
library(viridis)
library(scales)
library(rpart)
library(rpart.plot)

I found some helpful functions in R, so we could have a look on our data. We will start with a structure, than we will get some statistic data and take a head() of the data

str(dt_houses)
Classes ‘data.table’ and 'data.frame':  545 obs. of  13 variables:
 $ price           : int  13300000 12250000 12250000 12215000 11410000 10850000 10150000 10150000 9870000 9800000 ...
 $ area            : int  7420 8960 9960 7500 7420 7500 8580 16200 8100 5750 ...
 $ bedrooms        : int  4 4 3 4 4 3 4 5 4 3 ...
 $ bathrooms       : int  2 4 2 2 1 3 3 3 1 2 ...
 $ stories         : int  3 4 2 2 2 1 4 2 2 4 ...
 $ mainroad        : chr  "yes" "yes" "yes" "yes" ...
 $ guestroom       : chr  "no" "no" "no" "no" ...
 $ basement        : chr  "no" "no" "yes" "yes" ...
 $ hotwaterheating : chr  "no" "no" "no" "no" ...
 $ airconditioning : chr  "yes" "yes" "no" "yes" ...
 $ parking         : int  2 3 2 3 2 2 2 0 2 1 ...
 $ prefarea        : chr  "yes" "no" "yes" "yes" ...
 $ furnishingstatus: chr  "furnished" "furnished" "semi-furnished" "furnished" ...
 - attr(*, ".internal.selfref")=<externalptr> 


Statistic data:

summary(dt_houses[, .(price, area, bedrooms, bathrooms, stories, parking)])
     price               area          bedrooms       bathrooms        stories         parking      
 Min.   : 1750000   Min.   : 1650   Min.   :1.000   Min.   :1.000   Min.   :1.000   Min.   :0.0000  
 1st Qu.: 3430000   1st Qu.: 3600   1st Qu.:2.000   1st Qu.:1.000   1st Qu.:1.000   1st Qu.:0.0000  
 Median : 4340000   Median : 4600   Median :3.000   Median :1.000   Median :2.000   Median :0.0000  
 Mean   : 4766729   Mean   : 5151   Mean   :2.965   Mean   :1.286   Mean   :1.806   Mean   :0.6936  
 3rd Qu.: 5740000   3rd Qu.: 6360   3rd Qu.:3.000   3rd Qu.:2.000   3rd Qu.:2.000   3rd Qu.:1.0000  
 Max.   :13300000   Max.   :16200   Max.   :6.000   Max.   :4.000   Max.   :4.000   Max.   :3.0000  


and this is a sample of our dataset:

head(dt_houses)

I would like to start from density of a main values, which are from my domain knowledge are important in price of the properties

Price density:

ggplot(data = dt_houses, aes(x = price)) + 
  geom_density(fill="#f1b147", color="#f1b147", alpha=0.25) + 
  labs(
    x = 'Price',
    y = 'Density'
  ) +
  geom_vline(xintercept = mean(dt_houses$price), linetype="dashed") + 
  scale_x_continuous(labels = label_number(scale = 1e-6, suffix = "M")) + 
  theme_minimal() + 
  theme(axis.line = element_line(color = "#000000"))

This density plot visualizes the distribution of house prices, showing that most houses are priced around 4-5 million, with a right-skewed distribution (some higher-priced houses pulling the mean up). The dashed vertical line represents the mean price (~5M). The plot highlights that while most houses fall within a moderate price range, some expensive properties extend beyond 10M.

Area density:

ggplot(data = dt_houses, aes(x = area)) + 
  geom_density(fill="#f1b147", color="#f1b147", alpha=0.25) + 
  labs(
    x = 'Price',
    y = 'Density'
  ) +
  theme_minimal() + 
  theme(axis.line = element_line(color = "#000000"))

The area density plot looks similar to price density plot and can also make sense, because if house has a bigger area, the higher cost is quite expected. This plot shows that most houses are having area in range ~3000-5000. But some properties have area more than 12000.


Next plot will visualize the distribution of price depending on area.

ggplot() + 
  geom_point(data = dt_houses, aes(x = area, y = price, color = parking)) +
  scale_y_continuous(labels = label_number(scale = 1e-6, suffix = "M")) + 
  theme_minimal() + 
  theme(axis.line = element_line(color = "#000000"))

This scatter plot visualizes the relationship between house area (x-axis) and price (y-axis), with color indicating the number of parking spaces. It shows a positive correlation between area and price—larger houses tend to be more expensive. However, there is some variability, as some large houses have relatively lower prices. The color gradient suggests that houses with more parking spaces (lighter blue) tend to be higher in price and larger in area.

The next plot, which I am going to do is a boxplot and I want to use bedrooms as a factor variable on x axis and price on y-axis, to get an overall understanding, how amount of bedrooms affect price.

ggplot(data = dt_houses, aes(x = factor(bedrooms), y = price)) +
  geom_boxplot() + 
  theme_minimal() 

Boxplot shows, that on average, houses with more bedrooms have higher prices, but around 4-6 bedrooms, 1 quantile stagnates, and so does median price. There are some outliers, but not too much.

It is also interesting to take a look at distribution of bedrooms, so next plot would be a histogram, because I want to know, which amount of bedrooms is the most “popular” in the whole dataset.

ggplot(data = dt_houses, aes(x = bedrooms)) + 
  geom_histogram(fill="#2f9e44", color="#2f9e44", alpha=0.25) + 
  geom_vline(xintercept = mean(dt_houses$bedrooms), linetype="dashed") + 
  theme_minimal() + 
  theme(axis.line = element_line(color = "#000000"))

mean of the bedrooms:

mean(dt_houses$bedrooms)
[1] 2.965138

From this visualization we can mention, that the most of the houses have 2, 3 or 4 rooms. 1, 5 and 6 rooms are not as popular in this dataset.

Let’s have a look at histogram of stories:

ggplot(data = dt_houses, aes(x = stories)) + 
  geom_histogram(fill="#2f9e44", color="#2f9e44", alpha=0.25) + 
  geom_vline(xintercept = mean(dt_houses$stories), linetype="dashed") + 
  theme_minimal() + 
  theme(axis.line = element_line(color = "#000000"))

mean(dt_houses$stories)
[1] 1.805505

This plot shows that most popular amount of stories are 1 and 2. 3 and 4 makeing less than 100 houses together.

Bathrooms are also interesting variable, so let’s take a look at histogram and a Boxplot bathrooms and price:

ggplot(data = dt_houses, aes(x = bathrooms)) + 
  geom_histogram(fill="#2f9e44", color="#2f9e44", alpha=0.25) + 
  geom_vline(xintercept = mean(dt_houses$bathrooms), linetype="dashed") + 
  theme_minimal() + 
  theme(axis.line = element_line(color = "#000000"))

ggplot(data = dt_houses, aes(x = factor(bathrooms), y = price)) +
  geom_boxplot() + 
  theme_minimal() 

here it is also almost obvious, that, if we have more bathrooms, price will be also up. Only one disadvantage, that in my dataset I do not have enough data about properties with 3 or 4 bathrooms, I have some on 3, but really luck on 4.

Furnishing is also important, many people search for apartments with furniture, but furniture could be not in a best shape or buyer may do not like the style. So from my opinion, it is not as strong(in prediction), as for example area.

How much real estate furnished or not:

ggplot(data = dt_houses, aes(x = factor(furnishingstatus), fill = factor(furnishingstatus))) + 
  geom_bar(color="#ced4da", alpha=0.25) + 
  scale_fill_viridis_d(option = "D") + 
  labs(title = "Bar Chart with Different Colors", 
       x = "Furnishing Status", 
       y = "Count") + 
  theme_minimal() + 
  theme(axis.line = element_line(color = "#000000"))

We can see, that most of the houses are semi-furnished. which is also logical, because when we sell a house or apartment, probably we would take in most of the cases the most valuable things for us and furniture included.

Now, it would be great, to look at price and area distribution in differently furnished properties

ggplot(data = dt_houses, aes(y = price, x = area)) + 
  geom_point(data = dt_houses, aes(y = price, x = area, color = bedrooms)) +
  geom_hline(yintercept = mean(dt_houses$price), linetype='dashed') + 
  facet_grid(.~furnishingstatus) +
  scale_y_continuous(labels = label_number(scale = 1e-6, suffix = "M")) +
  scale_color_distiller(type = "seq", palette = "Greens") +
  theme_minimal() + 
  theme(axis.line = element_line(color = "#000000"))

Also, on average, you can notice, that unfurnished houses, are less expensive.

We can also take a look on some pie charts:


dt_mainroad_counts <- as.data.frame(table(dt_houses$mainroad)) #table() - creates frequency table
colnames(dt_mainroad_counts) <- c("mainroad_status", "count")
dt_mainroad_counts$percentage <- round(dt_mainroad_counts$count / sum(dt_mainroad_counts$count) * 100, 1)

ggplot(data = dt_mainroad_counts, aes(x = "", y = count, fill = mainroad_status)) +
  geom_bar(stat = "identity", width = 1, color = "white") +
  coord_polar("y", start = 0) +
  geom_text(aes(label = paste0(percentage, "%")), 
            position = position_stack(vjust = 0.5), color = "white", size = 4) +  
  theme_void() +  
  scale_fill_manual(values = c("#F1B147", "#47B1F1")) + 
  labs(
    title = "Distribution of Mainroad Status",
    fill = "Mainroad Status"
  )

Almost 86 percent of houses have main road, so maybe this won’t be a strong predictor variable.


dt_airconditioning_counts <- as.data.frame(table(dt_houses$airconditioning)) #table() - creates frequency table
colnames(dt_airconditioning_counts) <- c("airconditioning_status", "count")
dt_airconditioning_counts$percentage <- round(dt_airconditioning_counts$count / sum(dt_airconditioning_counts$count) * 100, 1)

ggplot(data = dt_airconditioning_counts, aes(x = "", y = count, fill = airconditioning_status)) +
  geom_bar(stat = "identity", width = 1, color = "white") +
  coord_polar("y", start = 0) +
  geom_text(aes(label = paste0(percentage, "%")), 
            position = position_stack(vjust = 0.5), color = "white", size = 4) +  
  theme_void() +  
  scale_fill_manual(values = c("#F1B147", "#47B1F1")) + 
  labs(
    title = "Distribution of Airconditioning status",
    fill = "Airconditioning Status"
  )

Here 68.4 percent has airconditioning, but I do not know, how it will affect predictions.

I think that would be enough exploration and we can start with our first model.

Models 1 & 2

First, I would like to start pretty simple with linear model.

I consider to take all variables to my model, because they all seem to be very important.

But before we start, I want to introduce a data table, which will be very useful in the end of this course work.

dt_features_performance <- data.table("price_lm_rmse" = c(0, 0, 0, 0, 0), "price_tree_rmse" = c(0, 0, 0, 0, 0), "feature" = c(0, 1, 2, 3, 4))

Linear model

I will use lm function in R to find needed beta coefficients and create my model

price_lm <- lm(formula = price ~ area + bedrooms + hotwaterheating + airconditioning + stories + mainroad + parking + furnishingstatus + bathrooms + guestroom + basement + prefarea, data = dt_houses)

summary(price_lm)

Call:
lm(formula = price ~ area + bedrooms + hotwaterheating + airconditioning + 
    stories + mainroad + parking + furnishingstatus + bathrooms + 
    guestroom + basement + prefarea, data = dt_houses)

Residuals:
     Min       1Q   Median       3Q      Max 
-2619718  -657322   -68409   507176  5166695 

Coefficients:
                                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)                      42771.69  264313.31   0.162 0.871508    
area                               244.14      24.29  10.052  < 2e-16 ***
bedrooms                        114787.56   72598.66   1.581 0.114445    
hotwaterheatingyes              855447.15  223152.69   3.833 0.000141 ***
airconditioningyes              864958.31  108354.51   7.983 8.91e-15 ***
stories                         450848.00   64168.93   7.026 6.55e-12 ***
mainroadyes                     421272.59  142224.13   2.962 0.003193 ** 
parking                         277107.10   58525.89   4.735 2.82e-06 ***
furnishingstatussemi-furnished  -46344.62  116574.09  -0.398 0.691118    
furnishingstatusunfurnished    -411234.39  126210.56  -3.258 0.001192 ** 
bathrooms                       987668.11  103361.98   9.555  < 2e-16 ***
guestroomyes                    300525.86  131710.22   2.282 0.022901 *  
basementyes                     350106.90  110284.06   3.175 0.001587 ** 
prefareayes                     651543.80  115682.34   5.632 2.89e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1068000 on 531 degrees of freedom
Multiple R-squared:  0.6818,    Adjusted R-squared:  0.674 
F-statistic: 87.52 on 13 and 531 DF,  p-value: < 2.2e-16

We got 0.68 R-squared, which is not that bad for a model just made up. But that’s not all, I will try to do better here, but first, another model.

But I would like to measure performance of my models with RMSE, so I will calculate RMSE for linear model.

price_lm_rmse <- mean(sqrt(abs(price_lm$residuals)))

price_lm_rmse
[1] 797.382

Tree Model

I think this model could perform better, because there some variables which can affect this model not only linearly, but the other way, in this case tree model can show better performance.

In this coursework will be used rpart to create a regression tree.

prices_tree <- rpart(data = dt_houses, formula = price ~ area + bedrooms + hotwaterheating + airconditioning + stories + mainroad + parking + furnishingstatus + bathrooms + guestroom + basement + prefarea, method = 'anova')

prp(prices_tree, digits = -3)

printcp(prices_tree)

Regression tree:
rpart(formula = price ~ area + bedrooms + hotwaterheating + airconditioning + 
    stories + mainroad + parking + furnishingstatus + bathrooms + 
    guestroom + basement + prefarea, data = dt_houses, method = "anova")

Variables actually used in tree construction:
[1] airconditioning  area             basement         bathrooms        furnishingstatus parking         

Root node error: 1.9032e+15/545 = 3.4921e+12

n= 545 

         CP nsplit rel error  xerror     xstd
1  0.304946      0   1.00000 1.00456 0.085202
2  0.094553      1   0.69505 0.72070 0.063329
3  0.053743      2   0.60050 0.62700 0.054969
4  0.026381      3   0.54676 0.59647 0.051655
5  0.024922      4   0.52038 0.59399 0.052224
6  0.022993      5   0.49546 0.58608 0.052820
7  0.021374      6   0.47246 0.56090 0.051090
8  0.015261      7   0.45109 0.55862 0.050557
9  0.013952      8   0.43583 0.56672 0.051222
10 0.012386      9   0.42188 0.55446 0.050699
11 0.010000     10   0.40949 0.54623 0.050358

Now after I have built with the help of rpart tree model based on my dataset, let us explore it:

prices_tree
n= 545 

node), split, n, deviance, yval
      * denotes terminal node

 1) root 545 1.903208e+15 4766729  
   2) area< 5954 361 6.066751e+14 4029993  
     4) bathrooms< 1.5 293 3.297298e+14 3773561  
       8) area< 4016 174 1.437122e+14 3431227  
        16) furnishingstatus=unfurnished 78 4.036605e+13 2977962 *
        17) furnishingstatus=furnished,semi-furnished 96 7.430067e+13 3799505 *
       9) area>=4016 119 1.358098e+14 4274118 *
     5) bathrooms>=1.5 68 1.746610e+14 5134912  
      10) airconditioning=no 44 7.024826e+13 4563682 *
      11) airconditioning=yes 24 6.373358e+13 6182167 *
   3) area>=5954 184 7.161564e+14 6212174  
     6) bathrooms< 1.5 108 2.869179e+14 5382579  
      12) airconditioning=no 65 1.170629e+14 4843569  
        24) basement=no 38 5.226335e+13 4304816 *
        25) basement=yes 27 3.824662e+13 5601815 *
      13) airconditioning=yes 43 1.224240e+14 6197360 *
     7) bathrooms>=1.5 76 2.492851e+14 7391072  
      14) parking< 1.5 51 7.184700e+13 6859794 *
      15) parking>=1.5 25 1.336772e+14 8474878  
        30) airconditioning=no 10 5.146311e+13 7285600 *
        31) airconditioning=yes 15 5.864106e+13 9267729 *

We can see, that we have 31 Nodes, I think for this kind of dataset it may be okay.

Now it would be great to prune the tree, because I do not want my tree to overfit:

plotcp(prices_tree)

This is complexity of this tree. We need the lowest complexity, to get as few leafs as possible to get the best performance, so that tree won’t overfit the data.

prices_tree_min_cp <- prices_tree$cptable[which.min(prices_tree$cptable[, "xerror"]), "CP"]
model_tree <- prune(prices_tree, cp = prices_tree_min_cp )
prp(prices_tree,digits = -3)

after we pruned the tree, let’s calculate the RMSE for the tree model

prices_tree_pred <- predict(prices_tree, dt_houses[, c("area","bathrooms", "bedrooms", "hotwaterheating", "airconditioning", "parking", "stories", "mainroad", "furnishingstatus", "guestroom", "basement", "prefarea")])
prices_tree_rmse <- mean(sqrt(abs(dt_houses$price - prices_tree_pred)))

prices_tree_rmse
[1] 860.0223

Comparing two models

price linear model has a RMSE of

price_lm_rmse
[1] 797.382

price tree model has a RMSE of

prices_tree_rmse
[1] 860.0223

It is surprising for me, as for a person who does not have a lot of experience in modelling, that linear model performs better than tree model by approx. 7.28%.

100 - price_lm_rmse / prices_tree_rmse * 100
[1] 7.283574

collecting data for my statistics in the end

dt_features_performance$price_lm_rmse[dt_features_performance$feature == 0] <- price_lm_rmse
dt_features_performance$price_tree_rmse[dt_features_performance$feature == 0] <- prices_tree_rmse

Feature Engineering

Here I would like to try all ideas and observations, which I’ve had through my course work. As I have a lot of binary variables and they are already encoded by R’s lm library I would use factor variables such as bedrooms, bathrooms and stories.

Feature 1

Bedrooms as a factor variable

I want to use bedroom variable as a factor, to do that I will delete original bedrooms and add instead bedrooms factor.

Linear Model

Let’s try Model with a new factor variable.

price_lm <- lm(formula = price ~ area + factor(bedrooms) + hotwaterheating + airconditioning + stories + mainroad + parking + furnishingstatus + bathrooms + guestroom + basement + prefarea, data = dt_houses)

summary(price_lm)

Call:
lm(formula = price ~ area + factor(bedrooms) + hotwaterheating + 
    airconditioning + stories + mainroad + parking + furnishingstatus + 
    bathrooms + guestroom + basement + prefarea, data = dt_houses)

Residuals:
     Min       1Q   Median       3Q      Max 
-2603715  -652407   -78857   515376  5210924 

Coefficients:
                                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)                     357446.85  775559.37   0.461 0.645068    
area                               244.65      24.48   9.992  < 2e-16 ***
factor(bedrooms)2              -114030.93  766523.74  -0.149 0.881797    
factor(bedrooms)3                44379.14  767625.38   0.058 0.953919    
factor(bedrooms)4                95121.74  776261.47   0.123 0.902519    
factor(bedrooms)5               225794.30  840612.20   0.269 0.788337    
factor(bedrooms)6               725852.04 1075980.80   0.675 0.500228    
hotwaterheatingyes              855162.11  225019.54   3.800 0.000161 ***
airconditioningyes              865512.39  108940.97   7.945 1.19e-14 ***
stories                         447940.57   66210.11   6.765 3.54e-11 ***
mainroadyes                     420192.91  143761.60   2.923 0.003617 ** 
parking                         277396.50   58975.46   4.704 3.27e-06 ***
furnishingstatussemi-furnished  -42608.78  117741.15  -0.362 0.717583    
furnishingstatusunfurnished    -412029.93  126841.54  -3.248 0.001235 ** 
bathrooms                       995395.42  104634.07   9.513  < 2e-16 ***
guestroomyes                    302160.05  132199.82   2.286 0.022672 *  
basementyes                     348044.60  111709.52   3.116 0.001936 ** 
prefareayes                     646125.01  117161.83   5.515 5.48e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1071000 on 527 degrees of freedom
Multiple R-squared:  0.6822,    Adjusted R-squared:  0.6719 
F-statistic: 66.54 on 17 and 527 DF,  p-value: < 2.2e-16
price_lm_rmse <- mean(sqrt(abs(price_lm$residuals)))

price_lm_rmse
[1] 796.3947

Now the RMSE is a little bit better. Approximately by 0.12%. Next I am going to test this variable on the tree model.

100 - price_lm_rmse / 797.382 * 100
[1] 0.1238174

Tree Model

prices_tree <- rpart(data = dt_houses, formula = price ~ area + factor(bedrooms) + hotwaterheating + airconditioning + stories + mainroad + parking + furnishingstatus + bathrooms + guestroom + basement + prefarea, method = 'anova')

prp(prices_tree, digits = -3)

I think, that in feature enginieering, I won’t plot any tree complexity and explore tree itself, because here the main focus is on the benchmarking and comparing two models with new features. Let’s prune the model and measure RMSE

# pruning
prices_tree_min_cp <- prices_tree$cptable[which.min(prices_tree$cptable[, "xerror"]), "CP"]
model_tree <- prune(prices_tree, cp = prices_tree_min_cp )

# predicting
prices_tree_pred <- predict(prices_tree, dt_houses[, c("area","bathrooms", "bedrooms", "hotwaterheating", "airconditioning", "parking", "stories", "mainroad", "furnishingstatus", "guestroom", "basement", "prefarea")])

#calculating RMSE
prices_tree_rmse <- mean(sqrt(abs(dt_houses$price - prices_tree_pred)))

prices_tree_rmse
[1] 860.0223

It performs the same, and I think it should be like that, because tree is not sensible for factor variables. It is still a number. It is interpreted other way by linear Model, but for the tree it is the same.

Here I am adding my variables to create a small chart in the end.

dt_features_performance$price_lm_rmse[dt_features_performance$feature == 1] <- price_lm_rmse
dt_features_performance$price_tree_rmse[dt_features_performance$feature == 1] <- prices_tree_rmse

Comparing

With new ‘factor(bedroom)’ feature, linear model performs better: 796.3947 - RMSE On the other side tree model with new feature has not improved.

Linear model is still better, but may be there is some chances, we have 3 more features.

Feature 2

Moving area closer to Gaussian (log transformation)

what if we will try to bring the area variable closer to Gaussian with log transformation, because area density is skewed to the left, log transformation can help us to normalize the variable.

Linear Model

I am going to create a new column in my data table, which will be called area_log and will contain ln(area[i]), where i is the index of the row.

dt_houses[, area_log := log(area)]

Let’s also visualize it. Here function looks much balanced and I think it will work better.

ggplot(data = dt_houses, aes(x = area_log)) + 
  geom_density(fill="#f1b147", color="#f1b147", alpha=0.25) + 
  labs(
    x = 'Price',
    y = 'Density'
  ) +
  theme_minimal() + 
  theme(axis.line = element_line(color = "#000000"))

Now I want to run the model with a second new feature.

price_lm <- lm(formula = price ~ area + factor(bedrooms) + hotwaterheating + airconditioning + stories + mainroad + parking + furnishingstatus + bathrooms + guestroom + basement + prefarea + area_log, data = dt_houses)

summary(price_lm)

Call:
lm(formula = price ~ area + factor(bedrooms) + hotwaterheating + 
    airconditioning + stories + mainroad + parking + furnishingstatus + 
    bathrooms + guestroom + basement + prefarea + area_log, data = dt_houses)

Residuals:
     Min       1Q   Median       3Q      Max 
-2635466  -652907   -65980   484713  5180753 

Coefficients:
                                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)                    -8.627e+06  3.583e+06  -2.408 0.016390 *  
area                            3.981e+01  8.340e+01   0.477 0.633303    
factor(bedrooms)2              -6.539e+04  7.627e+05  -0.086 0.931708    
factor(bedrooms)3               1.017e+05  7.639e+05   0.133 0.894098    
factor(bedrooms)4               1.312e+05  7.723e+05   0.170 0.865155    
factor(bedrooms)5               3.657e+05  8.380e+05   0.436 0.662678    
factor(bedrooms)6               7.477e+05  1.070e+06   0.699 0.485144    
hotwaterheatingyes              8.513e+05  2.238e+05   3.803 0.000160 ***
airconditioningyes              8.208e+05  1.098e+05   7.478 3.17e-13 ***
stories                         4.448e+05  6.587e+04   6.753 3.84e-11 ***
mainroadyes                     3.474e+05  1.458e+05   2.383 0.017527 *  
parking                         2.701e+05  5.873e+04   4.598 5.35e-06 ***
furnishingstatussemi-furnished -6.605e+04  1.175e+05  -0.562 0.574204    
furnishingstatusunfurnished    -4.314e+05  1.264e+05  -3.413 0.000691 ***
bathrooms                       9.904e+05  1.041e+05   9.514  < 2e-16 ***
guestroomyes                    2.435e+05  1.335e+05   1.824 0.068656 .  
basementyes                     3.628e+05  1.113e+05   3.261 0.001184 ** 
prefareayes                     6.678e+05  1.169e+05   5.715 1.84e-08 ***
area_log                        1.193e+06  4.644e+05   2.568 0.010508 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1066000 on 526 degrees of freedom
Multiple R-squared:  0.6861,    Adjusted R-squared:  0.6754 
F-statistic: 63.88 on 18 and 526 DF,  p-value: < 2.2e-16
price_lm_rmse <- mean(sqrt(abs(price_lm$residuals)))
price_lm_rmse
[1] 792.3163

Success! It makes less errors. Previous we had RMSE of 796.3947, now it is 792.3163. Also 0.51% performance improvement.

100 - price_lm_rmse / 796.3947 * 100
[1] 0.512108

Tree Model

It is tree’s turn, I want to run new feature on tree Model.

prices_tree <- rpart(data = dt_houses, formula = price ~ area + factor(bedrooms) + hotwaterheating + airconditioning + stories + mainroad + parking + furnishingstatus + bathrooms + guestroom + basement + prefarea  + area_log, method = 'anova')

prp(prices_tree, digits = -3)

Now I will prune the tree and then calculate the RMSE for the model with this new feature.

prices_tree_min_cp <- prices_tree$cptable[which.min(prices_tree$cptable[, "xerror"]), "CP"]
model_tree <- prune(prices_tree, cp = prices_tree_min_cp )
prp(prices_tree,digits = -3)

and calculating the error

prices_tree_pred <- predict(prices_tree, dt_houses[, c("area","bathrooms", "bedrooms", "hotwaterheating", "airconditioning", "parking", "stories", "mainroad", "furnishingstatus", "guestroom", "basement", "prefarea", "area_log")])
prices_tree_rmse <- mean(sqrt(abs(dt_houses$price - prices_tree_pred)))

prices_tree_rmse
[1] 860.0223

Yep, there is no gain in performance, and I could probably say why. Linear model gains performance when we normalize variables, because this algorithm is sensitive to Gaussian, but the tree model, does not “care” so much about density of the variables, because it does not calculate “distance” between points and it is great in working with non-linar dependencies. This is my prediction, but I could be also wrong.

Comparing

So there are RMSE from linear model: 792.3163 and RMSE from tree: 850.561. Linear model’s performance is better than tree’s

dt_features_performance$price_lm_rmse[dt_features_performance$feature == 2] <- price_lm_rmse
dt_features_performance$price_tree_rmse[dt_features_performance$feature == 2] <- prices_tree_rmse

Relevant statistics theory

I think, this could be a good Idea to take a loot at a correlation between variables, but from Data exploration I can already say, that area correlates with price.

Here we are, correlation plot:

ggcorrplot(corr = cor(dt_houses[, .(price, area, bedrooms, bathrooms, stories, parking)]), 
           hc.order = TRUE,
           lab = TRUE)

Hm, correlation plot does not look as great, as I have expected, but the strongest correlation with price is area and amount of bathrooms.

Feature 3

Treat bathrooms as a factor variable

I got an Idea, we have bathrooms, and they are in range from 1 to 4.What if we will treat each amount of bathrooms as a factor variable. Because it is possible that home with 2 bathrooms is drastically more expensive than a house with 1, and the one with 3 bathrooms is super costly

Linear Model

price_lm <- lm(formula = price ~ area + factor(bedrooms) + hotwaterheating + airconditioning + stories + mainroad + parking + furnishingstatus + factor(bathrooms) + guestroom + basement + prefarea + area_log, data = dt_houses)

summary(price_lm)

Call:
lm(formula = price ~ area + factor(bedrooms) + hotwaterheating + 
    airconditioning + stories + mainroad + parking + furnishingstatus + 
    factor(bathrooms) + guestroom + basement + prefarea + area_log, 
    data = dt_houses)

Residuals:
     Min       1Q   Median       3Q      Max 
-2611477  -654190   -71264   502239  5253586 

Coefficients:
                                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)                    -8.206e+06  3.598e+06  -2.281 0.022960 *  
area                            2.480e+01  8.385e+01   0.296 0.767509    
factor(bedrooms)2              -5.979e+04  7.613e+05  -0.079 0.937432    
factor(bedrooms)3               1.192e+05  7.625e+05   0.156 0.875794    
factor(bedrooms)4               1.483e+05  7.709e+05   0.192 0.847515    
factor(bedrooms)5               3.700e+05  8.374e+05   0.442 0.658769    
factor(bedrooms)6               7.965e+05  1.069e+06   0.745 0.456418    
hotwaterheatingyes              8.678e+05  2.236e+05   3.881 0.000117 ***
airconditioningyes              8.282e+05  1.100e+05   7.532 2.21e-13 ***
stories                         4.440e+05  6.599e+04   6.728 4.51e-11 ***
mainroadyes                     3.479e+05  1.455e+05   2.391 0.017164 *  
parking                         2.613e+05  5.880e+04   4.445 1.08e-05 ***
furnishingstatussemi-furnished -6.554e+04  1.179e+05  -0.556 0.578442    
furnishingstatusunfurnished    -4.361e+05  1.267e+05  -3.442 0.000624 ***
factor(bathrooms)2              9.004e+05  1.208e+05   7.453 3.81e-13 ***
factor(bathrooms)3              2.166e+06  3.554e+05   6.095 2.13e-09 ***
factor(bathrooms)4              4.801e+06  1.090e+06   4.404 1.29e-05 ***
guestroomyes                    2.479e+05  1.333e+05   1.860 0.063503 .  
basementyes                     3.694e+05  1.111e+05   3.324 0.000949 ***
prefareayes                     6.795e+05  1.168e+05   5.819 1.03e-08 ***
area_log                        1.269e+06  4.665e+05   2.721 0.006732 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1064000 on 524 degrees of freedom
Multiple R-squared:  0.6885,    Adjusted R-squared:  0.6766 
F-statistic:  57.9 on 20 and 524 DF,  p-value: < 2.2e-16
price_lm_rmse <- mean(sqrt(abs(price_lm$residuals)))
price_lm_rmse
[1] 787.8693

And we gain a little bit more performance. This is really great. Approx 1.19% better than the first model without features. But not every factor is used, may be there is a big difference between 1 and 2 bathrooms, That may be because the luck of data, because I have less than 5 units with 3 or 4 bathrooms overall in my dataset.

100 - price_lm_rmse / 797.382 * 100
[1] 1.192997

Tree Model

Let us try tree model now.

prices_tree <- rpart(data = dt_houses, formula = price ~ area + factor(bedrooms) + hotwaterheating + airconditioning + stories + mainroad + parking + furnishingstatus + factor(bathrooms) + guestroom + basement + prefarea  + area_log, method = 'anova')

prp(prices_tree, digits = -3)

Now we have to prune the tree and then make predictions with RMSE calculations.

prices_tree_min_cp <- prices_tree$cptable[which.min(prices_tree$cptable[, "xerror"]), "CP"]
model_tree <- prune(prices_tree, cp = prices_tree_min_cp )


prices_tree_pred <- predict(prices_tree, dt_houses[, c("area","bathrooms", "bedrooms", "hotwaterheating", "airconditioning", "parking", "stories", "mainroad", "furnishingstatus", "guestroom", "basement", "prefarea", "area_log")])
prices_tree_rmse <- mean(sqrt(abs(dt_houses$price - prices_tree_pred)))

prices_tree_rmse
[1] 842.1272

This is awesome, we are making ~ 17.8951 less errors, this is 2.08% less errors.

100 - prices_tree_rmse / 860.0223 * 100
[1] 2.080773

Comparing

This becomes interesting. While linear model has improved by 1.19%, tree model made bigger gain in performance: ~2.08%. This is ~2 times linear model gains.

dt_features_performance$price_lm_rmse[dt_features_performance$feature == 3] <- price_lm_rmse
dt_features_performance$price_tree_rmse[dt_features_performance$feature == 3] <- prices_tree_rmse

Feature 4

Stories as a factor variable

I want to treat my stories also as a categorical factor variable and to do that I will again use factor() function in R. I will try this feature in linear and tree model.

First, I would like to start as usual with linear model.

Linear model

# calculating and running model
price_lm <- lm(formula = price ~ area + factor(bedrooms) + hotwaterheating + airconditioning + factor(stories) + mainroad + parking + furnishingstatus + factor(bathrooms) + guestroom + basement + prefarea + area_log, data = dt_houses)

summary(price_lm)

Call:
lm(formula = price ~ area + factor(bedrooms) + hotwaterheating + 
    airconditioning + factor(stories) + mainroad + parking + 
    furnishingstatus + factor(bathrooms) + guestroom + basement + 
    prefarea + area_log, data = dt_houses)

Residuals:
     Min       1Q   Median       3Q      Max 
-2652504  -649820   -77111   493086  5266659 

Coefficients:
                                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)                    -7.345e+06  3.627e+06  -2.025 0.043379 *  
area                            2.909e+01  8.405e+01   0.346 0.729365    
factor(bedrooms)2              -4.467e+04  7.622e+05  -0.059 0.953291    
factor(bedrooms)3               1.732e+05  7.653e+05   0.226 0.821019    
factor(bedrooms)4               2.245e+05  7.756e+05   0.289 0.772357    
factor(bedrooms)5               4.580e+05  8.431e+05   0.543 0.587197    
factor(bedrooms)6               8.987e+05  1.075e+06   0.836 0.403518    
hotwaterheatingyes              8.801e+05  2.252e+05   3.908 0.000105 ***
airconditioningyes              8.176e+05  1.106e+05   7.392 5.80e-13 ***
factor(stories)2                3.474e+05  1.205e+05   2.884 0.004091 ** 
factor(stories)3                8.448e+05  2.079e+05   4.064 5.56e-05 ***
factor(stories)4                1.380e+06  2.156e+05   6.400 3.46e-10 ***
mainroadyes                     3.557e+05  1.459e+05   2.438 0.015118 *  
parking                         2.577e+05  5.927e+04   4.348 1.65e-05 ***
furnishingstatussemi-furnished -6.013e+04  1.182e+05  -0.509 0.611051    
furnishingstatusunfurnished    -4.422e+05  1.270e+05  -3.482 0.000540 ***
factor(bathrooms)2              8.894e+05  1.216e+05   7.317 9.62e-13 ***
factor(bathrooms)3              2.175e+06  3.563e+05   6.106 2.00e-09 ***
factor(bathrooms)4              4.715e+06  1.096e+06   4.303 2.01e-05 ***
guestroomyes                    2.451e+05  1.336e+05   1.834 0.067153 .  
basementyes                     3.782e+05  1.119e+05   3.379 0.000782 ***
prefareayes                     6.763e+05  1.186e+05   5.704 1.96e-08 ***
area_log                        1.216e+06  4.700e+05   2.587 0.009940 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1065000 on 522 degrees of freedom
Multiple R-squared:  0.6891,    Adjusted R-squared:  0.6759 
F-statistic: 52.58 on 22 and 522 DF,  p-value: < 2.2e-16
price_lm_rmse <- mean(sqrt(abs(price_lm$residuals)))
price_lm_rmse
[1] 784.9238

There is a little performance gain in linear model. It is 0.37%. Now let’s test this feature on Tree.

100 - price_lm_rmse / 787.8693 * 100
[1] 0.3738567

Tree Model

After my previous experience with bathrooms, I think here we could also see some performance gain, and I think it will be probably better than by linear model. I want now to run the tree model and then prune and predict, then measure the RMSE:

prices_tree <- rpart(data = dt_houses, formula = price ~ area + factor(bedrooms) + hotwaterheating + airconditioning + factor(stories) + mainroad + parking + furnishingstatus + factor(bathrooms) + guestroom + basement + prefarea + area_log, method = 'anova')

prp(prices_tree, digits = -3)

Now I am pruning and predicting. Then calculating RMSE, which I want to compare with the previous result.

# pruning 
prices_tree_min_cp <- prices_tree$cptable[which.min(prices_tree$cptable[, "xerror"]), "CP"]
model_tree <- prune(prices_tree, cp = prices_tree_min_cp )

# predicting
prices_tree_pred <- predict(prices_tree, dt_houses[, c("area","bathrooms", "bedrooms", "hotwaterheating", "airconditioning", "parking", "stories", "mainroad", "furnishingstatus", "guestroom", "basement", "prefarea", "area_log")])

# calculating RMSE
prices_tree_rmse <- mean(sqrt(abs(dt_houses$price - prices_tree_pred)))

prices_tree_rmse
[1] 842.1272

and it seems that, there is no gain in performance if we use stories as a factor in the tree model.

Comparing

With feature number 4, we have seen the gain in linear model, but the tree model, did not gain any performance. I think that the stories were not very strong variable in Tree model and in Linear model also, because in compare with such a variable like bathrooms, stories has gained 3 times for linear model and nothing for tree model.

After we included feature 4, linear model is still performing better than the tree model with the overall performance of 784.923 and 842.1271 respectively.

dt_features_performance$price_lm_rmse[dt_features_performance$feature == 4] <- price_lm_rmse
dt_features_performance$price_tree_rmse[dt_features_performance$feature == 4] <- prices_tree_rmse

Now when I have my data, this is my conclusion plot:

ggplot() + 
  geom_point(data = dt_features_performance, aes(x = feature, y = price_lm_rmse), 
             size = 4, color = "#1f77b4", alpha = 0.8) + 
  geom_line(data = dt_features_performance, aes(x = feature, y = price_lm_rmse), 
            color = "#1f77b4", linewidth = 1) + 
  geom_point(data = dt_features_performance, aes(x = feature, y = price_tree_rmse), 
             size = 4, color = "#ff7f0e", alpha = 0.8) + 
  geom_line(data = dt_features_performance, aes(x = feature, y = price_tree_rmse), 
            color = "#ff7f0e", linewidth = 1) + 
  labs(title = "Performance with Amount of Features", 
       x = "Amount of Features", 
       y = "Performance (RMSE)") + 
  theme_minimal() + 
  theme(
    axis.line = element_line(color = "#000000"),
    text = element_text(size = 14),
    plot.title = element_text(size = 16, face = "bold", hjust = 0.5)
  )

With this plot I wanted to show you my progress in creating features. On the x-axis you can see the number of features, from 0 to 4, because I have included the raw model in this plot. On the y-axis is the RMSE of the models.

The orange line is the tree model and the blue line is the linear model.

With more features overall, both models could perform better, but for this dataset and my implementation, the linear model performs better. When I started, I thought the tree model would perform much better.

Finally, I would like to mention that the tree model is lower in performance, but there was a really good boost with feature number 3. While all 4 features have improved the linear model incrementally with each new feature.


LS0tCnRpdGxlOiAiQ291cnNld29yayAtIERhdGEgU2NpZW5jZSBJIgphdXRob3I6ICJPbWFyIFpoYWR5a292LCAyMjAyMjA1MDMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgZmlnX3dpZHRoOiAxMAogICAgdGhlbWU6IHNwYWNlbGFiCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHllcwogIHdvcmRfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnMycKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OgogICAgZmlnX3dpZHRoOiAxMAogICAgdGhlbWU6IHNwYWNlbGFiCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCjxzY3JpcHQ+CiQoZG9jdW1lbnQpLnJlYWR5KGZ1bmN0aW9uKCkgewogICRpdGVtcyA9ICQoJ2RpdiNUT0MgbGknKTsKICAkaXRlbXMuZWFjaChmdW5jdGlvbihpZHgpIHsKICAgIG51bV91bCA9ICQodGhpcykucGFyZW50c1VudGlsKCcjVE9DJykubGVuZ3RoOwogICAgJCh0aGlzKS5jc3Moeyd0ZXh0LWluZGVudCc6IG51bV91bCAqIDEwLCAncGFkZGluZy1sZWZ0JzogMH0pOwogIH0pOwoKfSk7Cjwvc2NyaXB0PgoKYGBge3Igc2V0dXAsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9CmxpYnJhcnkoc3ZnbGl0ZSkKbGlicmFyeShrbml0cikKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZGF0YS50YWJsZSkpCmxpYnJhcnkoZ2dwbG90MikKa25pdHI6Om9wdHNfY2h1bmskc2V0KGRldiA9ICJzdmdsaXRlIikKCiMgUHV0IHlvdXIgZGF0YXNldCBpbiB0aGUgc2FtZSBmb2xkZXIgYXMgeW91ciBSIGZpbGUuIFRoaXMgY29kZSB3aWxsIHNldCB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5IGZvciB0aGlzIG5vdGVib29rIHRvIHRoZSBmb2xkZXIgd2hlcmUgdGhlIFIgZmlsZSBpcyBzdG9yZWQuIFRoaXMgd2F5IEkgY2FuIHJlcnVuIHlvdXIgY29kZSB3aXRob3V0IG1vZGlmaWNhdGlvbnMuCgpsaWJyYXJ5KHJzdHVkaW9hcGkpCnNldHdkKGRpcm5hbWUoZ2V0QWN0aXZlRG9jdW1lbnRDb250ZXh0KCkkcGF0aCkpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KClRoaXMgY291cnNld29yayBmb2N1c2VzIG9uIGhvdXNpbmcgcHJpY2VzLCB3aXRoIHRoZSBtYWluIG9iamVjdGl2ZSBiZWluZyB0byBwcmVkaWN0IHRoZSBwcmljZSBvZiBhIHByb3BlcnR5IGJhc2VkIG9uIHZhcmlvdXMgaW5wdXRzLiBUaGUgaW5wdXRzIGluY2x1ZGUgZmVhdHVyZXMgc3VjaCBhcyB0aGUgYXJlYSwgdGhlIG51bWJlciBhbmQgdHlwZXMgb2Ygcm9vbXMsIGFuZCBhZGRpdGlvbmFsIGZhY3RvcnMgbGlrZSB0aGUgYXZhaWxhYmlsaXR5IG9mIGEgbWFpbiByb2FkLCBob3Qgd2F0ZXIgaGVhdGluZywgYW5kIG1vcmUuCgpUaGUgZGVwZW5kZW50IHZhcmlhYmxlIGlzIHRoZSBwcmljZSwgYXMgaXQgaXMgdGhlIHByaW1hcnkgY29uY2VybiBmb3IgbW9zdCBwZW9wbGUgc2VhcmNoaW5nIGZvciBhIGhvdXNlLiBUaGUgZ29hbCBvZiB0aGlzIHdvcmsgaXMgdG8gcHJlZGljdCB0aGUgcHJpY2UgYmFzZWQgb24gZGl2ZXJzZSBpbnB1dHMsIHdoaWNoIGNvbnNpc3Qgb2YgbWl4ZWQgZGF0YSB0eXBlcywgc3VjaCBhczoKCiAgLSBOdW1lcmljYWwgdmFsdWVzCiAgLSBUZXh0LWJhc2VkIHJlc3BvbnNlcyBsaWtlICJ5ZXMiIG9yICJubyIKICAtIENhdGVnb3JpZXMgZm9yIGZ1cm5pc2hpbmcgc3RhdHVzLCBpbmNsdWRpbmcgImZ1cm5pc2hlZCwiICJzZW1pLWZ1cm5pc2hlZCwiIG9yICJub24tZnVybmlzaGVkLiIKClRoaXMgcHJvamVjdCBhZGRyZXNzZXMgYSByZWdyZXNzaW9uIHByb2JsZW0gYmVjYXVzZSB0aGUgb2JqZWN0aXZlIGlzIHRvIHByZWRpY3QgYSBudW1lcmljIHZhbHVl4oCUaW4gdGhpcyBjYXNlLCB0aGUgcHJpY2Ugb2YgdGhlIHByb3BlcnR5LgoKIyBDb2xsZWN0aW9uIC8gUHJlcGFyYXRpb24gCgpOb3cgd2UgYXJlIGdvaW5nIHRvIGltcG9ydCBvdXIgZGF0YXNldCBpbnRvIHRoaXMgcHJvamVjdC4KCmBgYHtyfQpkdF9ob3VzZXMgPC0gZnJlYWQoZmlsZSA9ICIuL2RhdGFzZXRzL1JlZ3Jlc3Npb25fc2V0LmNzdiIpCmBgYAoKPGJyPgpJIHdvdWxkIGxpa2UgdG8gY2hlY2ssIGlmIGkgaGF2ZSBzb21lIG51bGxpc2ggZGF0YSBpbiBteSBkYXRhc2V0LiBJIHRoaW5rIGl0IGlzIGEgZ29vZCBpZGVhIHRvIGdvIHRocm91Z2ggYWxsIHJvd3MgYW5kIGNvbHVtcyBhbmQgY2hlY2ssIGlmIHRoZXJlIGlzIGEgTkEuIEkgd2FudCB0byBjaGVjayBpdCB3aXRoIGJ1aWx0LWluIGZ1bmN0aW9uIGluIFIgKmNvbXBsZXRlLmNhc2VzKGRhdGFfdGFibGUpKi4gVGhpcyBmdW5jdGlvbiByZXR1cm5zIFRSVUUgb3IgRkFMU0UgaWYgcm93IGNvbnRhaW5zIGEgTkEgdmFsdWUuCgpgYGB7cn0KbmFzIDwtIGR0X2hvdXNlc1shY29tcGxldGUuY2FzZXMoZHRfaG91c2VzKV0KbmFzCmBgYAoKVGhhdCBsb29rcyBncmVhdCwgbm93IHdlIGNhbiBleHBsb3JlIG91ciBkYXRhc2V0IDopCgojIEV4cGxvcmF0aW9uCgpCZWZvcmUgd2Ugd2lsbCBleHBsb3JlIG91ciBkYXRhLCBJIHdhbnQgdG8gaW1wb3J0IGFsbCBsaWJyYXJpZXMsIHdoaWNoIHdlIHdpbGwgcHJvYmFibHkgdXNlOgoKYGBge3J9CmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShnZ2NvcnJwbG90KQpsaWJyYXJ5KGdnRXh0cmEpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JpZGdlcykKbGlicmFyeShnZ3NjaSkKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoc3ZnbGl0ZSkKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShycGFydCkKbGlicmFyeShycGFydC5wbG90KQpgYGAKCkkgZm91bmQgc29tZSBoZWxwZnVsIGZ1bmN0aW9ucyBpbiBSLCBzbyB3ZSBjb3VsZCBoYXZlIGEgbG9vayBvbiBvdXIgZGF0YS4gV2Ugd2lsbCBzdGFydCB3aXRoIGEgc3RydWN0dXJlLCB0aGFuIHdlIHdpbGwgZ2V0IHNvbWUgc3RhdGlzdGljIGRhdGEgYW5kIHRha2UgYSAqaGVhZCgpKiBvZiB0aGUgZGF0YQoKYGBge3J9CnN0cihkdF9ob3VzZXMpCmBgYAo8YnI+ClN0YXRpc3RpYyBkYXRhOgpgYGB7cn0Kc3VtbWFyeShkdF9ob3VzZXNbLCAuKHByaWNlLCBhcmVhLCBiZWRyb29tcywgYmF0aHJvb21zLCBzdG9yaWVzLCBwYXJraW5nKV0pCmBgYAoKPGJyPgphbmQgdGhpcyBpcyBhIHNhbXBsZSBvZiBvdXIgZGF0YXNldDoKCmBgYHtyfQpoZWFkKGR0X2hvdXNlcykKYGBgCgpJIHdvdWxkIGxpa2UgdG8gc3RhcnQgZnJvbSBkZW5zaXR5IG9mIGEgbWFpbiB2YWx1ZXMsIHdoaWNoIGFyZSBmcm9tIG15IGRvbWFpbiBrbm93bGVkZ2UgYXJlIGltcG9ydGFudCBpbiBwcmljZSBvZiB0aGUgcHJvcGVydGllcwoKUHJpY2UgZGVuc2l0eTogCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkdF9ob3VzZXMsIGFlcyh4ID0gcHJpY2UpKSArIAogIGdlb21fZGVuc2l0eShmaWxsPSIjZjFiMTQ3IiwgY29sb3I9IiNmMWIxNDciLCBhbHBoYT0wLjI1KSArIAogIGxhYnMoCiAgICB4ID0gJ1ByaWNlJywKICAgIHkgPSAnRGVuc2l0eScKICApICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuKGR0X2hvdXNlcyRwcmljZSksIGxpbmV0eXBlPSJkYXNoZWQiKSArIAogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9udW1iZXIoc2NhbGUgPSAxZS02LCBzdWZmaXggPSAiTSIpKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjMDAwMDAwIikpCmBgYApUaGlzIGRlbnNpdHkgcGxvdCB2aXN1YWxpemVzIHRoZSBkaXN0cmlidXRpb24gb2YgaG91c2UgcHJpY2VzLCBzaG93aW5nIHRoYXQgbW9zdCBob3VzZXMgYXJlIHByaWNlZCBhcm91bmQgNC01IG1pbGxpb24sIHdpdGggYSByaWdodC1za2V3ZWQgZGlzdHJpYnV0aW9uIChzb21lIGhpZ2hlci1wcmljZWQgaG91c2VzIHB1bGxpbmcgdGhlIG1lYW4gdXApLiBUaGUgZGFzaGVkIHZlcnRpY2FsIGxpbmUgcmVwcmVzZW50cyB0aGUgbWVhbiBwcmljZSAofjVNKS4gVGhlIHBsb3QgaGlnaGxpZ2h0cyB0aGF0IHdoaWxlIG1vc3QgaG91c2VzIGZhbGwgd2l0aGluIGEgbW9kZXJhdGUgcHJpY2UgcmFuZ2UsIHNvbWUgZXhwZW5zaXZlIHByb3BlcnRpZXMgZXh0ZW5kIGJleW9uZCAxME0uCgpBcmVhIGRlbnNpdHk6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkdF9ob3VzZXMsIGFlcyh4ID0gYXJlYSkpICsgCiAgZ2VvbV9kZW5zaXR5KGZpbGw9IiNmMWIxNDciLCBjb2xvcj0iI2YxYjE0NyIsIGFscGhhPTAuMjUpICsgCiAgbGFicygKICAgIHggPSAnUHJpY2UnLAogICAgeSA9ICdEZW5zaXR5JwogICkgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjMDAwMDAwIikpCmBgYApUaGUgYXJlYSBkZW5zaXR5IHBsb3QgbG9va3Mgc2ltaWxhciB0byBwcmljZSBkZW5zaXR5IHBsb3QgYW5kIGNhbiBhbHNvIG1ha2Ugc2Vuc2UsIGJlY2F1c2UgaWYgaG91c2UgaGFzIGEgYmlnZ2VyIGFyZWEsIHRoZSBoaWdoZXIgY29zdCBpcyBxdWl0ZSBleHBlY3RlZC4gVGhpcyBwbG90IHNob3dzIHRoYXQgbW9zdCBob3VzZXMgYXJlIGhhdmluZyBhcmVhIGluIHJhbmdlIH4zMDAwLTUwMDAuIEJ1dCBzb21lIHByb3BlcnRpZXMgaGF2ZSBhcmVhIG1vcmUgdGhhbiAxMjAwMC4KCjxicj4KCk5leHQgcGxvdCB3aWxsIHZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHByaWNlIGRlcGVuZGluZyBvbiBhcmVhLiAKCmBgYHtyfQpnZ3Bsb3QoKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IGR0X2hvdXNlcywgYWVzKHggPSBhcmVhLCB5ID0gcHJpY2UsIGNvbG9yID0gcGFya2luZykpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbGFiZWxfbnVtYmVyKHNjYWxlID0gMWUtNiwgc3VmZml4ID0gIk0iKSkgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzAwMDAwMCIpKQpgYGAKClRoaXMgc2NhdHRlciBwbG90IHZpc3VhbGl6ZXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGhvdXNlIGFyZWEgKHgtYXhpcykgYW5kIHByaWNlICh5LWF4aXMpLCB3aXRoIGNvbG9yIGluZGljYXRpbmcgdGhlIG51bWJlciBvZiBwYXJraW5nIHNwYWNlcy4gSXQgc2hvd3MgYSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGFyZWEgYW5kIHByaWNl4oCUbGFyZ2VyIGhvdXNlcyB0ZW5kIHRvIGJlIG1vcmUgZXhwZW5zaXZlLiBIb3dldmVyLCB0aGVyZSBpcyBzb21lIHZhcmlhYmlsaXR5LCBhcyBzb21lIGxhcmdlIGhvdXNlcyBoYXZlIHJlbGF0aXZlbHkgbG93ZXIgcHJpY2VzLiBUaGUgY29sb3IgZ3JhZGllbnQgc3VnZ2VzdHMgdGhhdCBob3VzZXMgd2l0aCBtb3JlIHBhcmtpbmcgc3BhY2VzIChsaWdodGVyIGJsdWUpIHRlbmQgdG8gYmUgaGlnaGVyIGluIHByaWNlIGFuZCBsYXJnZXIgaW4gYXJlYS4KClRoZSBuZXh0IHBsb3QsIHdoaWNoIEkgYW0gZ29pbmcgdG8gZG8gaXMgYSBib3hwbG90IGFuZCBJIHdhbnQgdG8gdXNlIGJlZHJvb21zIGFzIGEgZmFjdG9yIHZhcmlhYmxlIG9uIHggYXhpcyBhbmQgcHJpY2Ugb24geS1heGlzLCB0byBnZXQgYW4gb3ZlcmFsbCB1bmRlcnN0YW5kaW5nLCBob3cgYW1vdW50IG9mIGJlZHJvb21zIGFmZmVjdCBwcmljZS4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGR0X2hvdXNlcywgYWVzKHggPSBmYWN0b3IoYmVkcm9vbXMpLCB5ID0gcHJpY2UpKSArCiAgZ2VvbV9ib3hwbG90KCkgKyAKICB0aGVtZV9taW5pbWFsKCkgCmBgYAoKQm94cGxvdCBzaG93cywgdGhhdCBvbiBhdmVyYWdlLCBob3VzZXMgd2l0aCBtb3JlIGJlZHJvb21zIGhhdmUgaGlnaGVyIHByaWNlcywgYnV0IGFyb3VuZCA0LTYgYmVkcm9vbXMsIDEgcXVhbnRpbGUgc3RhZ25hdGVzLCBhbmQgc28gZG9lcyBtZWRpYW4gcHJpY2UuIFRoZXJlIGFyZSBzb21lIG91dGxpZXJzLCBidXQgbm90IHRvbyBtdWNoLgoKSXQgaXMgYWxzbyBpbnRlcmVzdGluZyB0byB0YWtlIGEgbG9vayBhdCBkaXN0cmlidXRpb24gb2YgYmVkcm9vbXMsIHNvIG5leHQgcGxvdCB3b3VsZCBiZSBhIGhpc3RvZ3JhbSwgYmVjYXVzZSBJIHdhbnQgdG8ga25vdywgd2hpY2ggYW1vdW50IG9mIGJlZHJvb21zIGlzIHRoZSBtb3N0ICJwb3B1bGFyIiBpbiB0aGUgd2hvbGUgZGF0YXNldC4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGR0X2hvdXNlcywgYWVzKHggPSBiZWRyb29tcykpICsgCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0iIzJmOWU0NCIsIGNvbG9yPSIjMmY5ZTQ0IiwgYWxwaGE9MC4yNSkgKyAKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuKGR0X2hvdXNlcyRiZWRyb29tcyksIGxpbmV0eXBlPSJkYXNoZWQiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjMDAwMDAwIikpCmBgYAptZWFuIG9mIHRoZSBiZWRyb29tczoKYGBge3J9Cm1lYW4oZHRfaG91c2VzJGJlZHJvb21zKQpgYGAKCkZyb20gdGhpcyB2aXN1YWxpemF0aW9uIHdlIGNhbiBtZW50aW9uLCB0aGF0IHRoZSBtb3N0IG9mIHRoZSBob3VzZXMgaGF2ZSAyLCAzIG9yIDQgcm9vbXMuIDEsIDUgYW5kIDYgcm9vbXMgYXJlIG5vdCBhcyBwb3B1bGFyIGluIHRoaXMgZGF0YXNldC4KCkxldCdzIGhhdmUgYSBsb29rIGF0IGhpc3RvZ3JhbSBvZiBzdG9yaWVzOiAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGR0X2hvdXNlcywgYWVzKHggPSBzdG9yaWVzKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShmaWxsPSIjMmY5ZTQ0IiwgY29sb3I9IiMyZjllNDQiLCBhbHBoYT0wLjI1KSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW4oZHRfaG91c2VzJHN0b3JpZXMpLCBsaW5ldHlwZT0iZGFzaGVkIikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzAwMDAwMCIpKQpgYGAKCmBgYHtyfQptZWFuKGR0X2hvdXNlcyRzdG9yaWVzKQpgYGAKClRoaXMgcGxvdCBzaG93cyB0aGF0IG1vc3QgcG9wdWxhciBhbW91bnQgb2Ygc3RvcmllcyBhcmUgMSBhbmQgMi4gMyBhbmQgNCBtYWtlaW5nIGxlc3MgdGhhbiAxMDAgaG91c2VzIHRvZ2V0aGVyLgoKQmF0aHJvb21zIGFyZSBhbHNvIGludGVyZXN0aW5nIHZhcmlhYmxlLCBzbyBsZXQncyB0YWtlIGEgbG9vayBhdCBoaXN0b2dyYW0gYW5kIGEgQm94cGxvdCBiYXRocm9vbXMgYW5kIHByaWNlOgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkdF9ob3VzZXMsIGFlcyh4ID0gYmF0aHJvb21zKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShmaWxsPSIjMmY5ZTQ0IiwgY29sb3I9IiMyZjllNDQiLCBhbHBoYT0wLjI1KSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW4oZHRfaG91c2VzJGJhdGhyb29tcyksIGxpbmV0eXBlPSJkYXNoZWQiKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjMDAwMDAwIikpCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGR0X2hvdXNlcywgYWVzKHggPSBmYWN0b3IoYmF0aHJvb21zKSwgeSA9IHByaWNlKSkgKwogIGdlb21fYm94cGxvdCgpICsgCiAgdGhlbWVfbWluaW1hbCgpIApgYGAKCmhlcmUgaXQgaXMgYWxzbyBhbG1vc3Qgb2J2aW91cywgdGhhdCwgaWYgd2UgaGF2ZSBtb3JlIGJhdGhyb29tcywgcHJpY2Ugd2lsbCBiZSBhbHNvIHVwLiBPbmx5IG9uZSBkaXNhZHZhbnRhZ2UsIHRoYXQgaW4gbXkgZGF0YXNldCBJIGRvIG5vdCBoYXZlIGVub3VnaCBkYXRhIGFib3V0IHByb3BlcnRpZXMgd2l0aCAzIG9yIDQgYmF0aHJvb21zLCBJIGhhdmUgc29tZSBvbiAzLCBidXQgcmVhbGx5IGx1Y2sgb24gNC4KCkZ1cm5pc2hpbmcgaXMgYWxzbyBpbXBvcnRhbnQsIG1hbnkgcGVvcGxlIHNlYXJjaCBmb3IgYXBhcnRtZW50cyB3aXRoIGZ1cm5pdHVyZSwgYnV0IGZ1cm5pdHVyZSBjb3VsZCBiZSBub3QgaW4gYSBiZXN0IHNoYXBlIG9yIGJ1eWVyIG1heSBkbyBub3QgbGlrZSB0aGUgc3R5bGUuIFNvIGZyb20gbXkgb3BpbmlvbiwgaXQgaXMgbm90IGFzIHN0cm9uZyhpbiBwcmVkaWN0aW9uKSwgYXMgZm9yIGV4YW1wbGUgYXJlYS4KCkhvdyBtdWNoIHJlYWwgZXN0YXRlIGZ1cm5pc2hlZCBvciBub3Q6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkdF9ob3VzZXMsIGFlcyh4ID0gZmFjdG9yKGZ1cm5pc2hpbmdzdGF0dXMpLCBmaWxsID0gZmFjdG9yKGZ1cm5pc2hpbmdzdGF0dXMpKSkgKyAKICBnZW9tX2Jhcihjb2xvcj0iI2NlZDRkYSIsIGFscGhhPTAuMjUpICsgCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Qob3B0aW9uID0gIkQiKSArIAogIGxhYnModGl0bGUgPSAiQmFyIENoYXJ0IHdpdGggRGlmZmVyZW50IENvbG9ycyIsIAogICAgICAgeCA9ICJGdXJuaXNoaW5nIFN0YXR1cyIsIAogICAgICAgeSA9ICJDb3VudCIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gIiMwMDAwMDAiKSkKYGBgCgpXZSBjYW4gc2VlLCB0aGF0IG1vc3Qgb2YgdGhlIGhvdXNlcyBhcmUgc2VtaS1mdXJuaXNoZWQuIHdoaWNoIGlzIGFsc28gbG9naWNhbCwgYmVjYXVzZSB3aGVuIHdlIHNlbGwgYSBob3VzZSBvciBhcGFydG1lbnQsIHByb2JhYmx5IHdlIHdvdWxkIHRha2UgaW4gbW9zdCBvZiB0aGUgY2FzZXMgdGhlIG1vc3QgdmFsdWFibGUgdGhpbmdzIGZvciB1cyBhbmQgZnVybml0dXJlIGluY2x1ZGVkLgoKTm93LCBpdCB3b3VsZCBiZSBncmVhdCwgdG8gbG9vayBhdCBwcmljZSBhbmQgYXJlYSBkaXN0cmlidXRpb24gaW4gZGlmZmVyZW50bHkgZnVybmlzaGVkIHByb3BlcnRpZXMKCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkdF9ob3VzZXMsIGFlcyh5ID0gcHJpY2UsIHggPSBhcmVhKSkgKyAKICBnZW9tX3BvaW50KGRhdGEgPSBkdF9ob3VzZXMsIGFlcyh5ID0gcHJpY2UsIHggPSBhcmVhLCBjb2xvciA9IGJlZHJvb21zKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW4oZHRfaG91c2VzJHByaWNlKSwgbGluZXR5cGU9J2Rhc2hlZCcpICsgCiAgZmFjZXRfZ3JpZCgufmZ1cm5pc2hpbmdzdGF0dXMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbGFiZWxfbnVtYmVyKHNjYWxlID0gMWUtNiwgc3VmZml4ID0gIk0iKSkgKwogIHNjYWxlX2NvbG9yX2Rpc3RpbGxlcih0eXBlID0gInNlcSIsIHBhbGV0dGUgPSAiR3JlZW5zIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICIjMDAwMDAwIikpCmBgYAoKQWxzbywgb24gYXZlcmFnZSwgeW91IGNhbiBub3RpY2UsIHRoYXQgdW5mdXJuaXNoZWQgaG91c2VzLCBhcmUgbGVzcyBleHBlbnNpdmUuCgpXZSBjYW4gYWxzbyB0YWtlIGEgbG9vayBvbiBzb21lIHBpZSBjaGFydHM6CgpgYGB7cn0KCmR0X21haW5yb2FkX2NvdW50cyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKGR0X2hvdXNlcyRtYWlucm9hZCkpICN0YWJsZSgpIC0gY3JlYXRlcyBmcmVxdWVuY3kgdGFibGUKY29sbmFtZXMoZHRfbWFpbnJvYWRfY291bnRzKSA8LSBjKCJtYWlucm9hZF9zdGF0dXMiLCAiY291bnQiKQpkdF9tYWlucm9hZF9jb3VudHMkcGVyY2VudGFnZSA8LSByb3VuZChkdF9tYWlucm9hZF9jb3VudHMkY291bnQgLyBzdW0oZHRfbWFpbnJvYWRfY291bnRzJGNvdW50KSAqIDEwMCwgMSkKCmdncGxvdChkYXRhID0gZHRfbWFpbnJvYWRfY291bnRzLCBhZXMoeCA9ICIiLCB5ID0gY291bnQsIGZpbGwgPSBtYWlucm9hZF9zdGF0dXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSwgY29sb3IgPSAid2hpdGUiKSArCiAgY29vcmRfcG9sYXIoInkiLCBzdGFydCA9IDApICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcGFzdGUwKHBlcmNlbnRhZ2UsICIlIikpLCAKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDQpICsgIAogIHRoZW1lX3ZvaWQoKSArICAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjRjFCMTQ3IiwgIiM0N0IxRjEiKSkgKyAKICBsYWJzKAogICAgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIE1haW5yb2FkIFN0YXR1cyIsCiAgICBmaWxsID0gIk1haW5yb2FkIFN0YXR1cyIKICApCgpgYGAKCkFsbW9zdCA4NiBwZXJjZW50IG9mIGhvdXNlcyBoYXZlIG1haW4gcm9hZCwgc28gbWF5YmUgdGhpcyB3b24ndCBiZSBhIHN0cm9uZyBwcmVkaWN0b3IgdmFyaWFibGUuCgoKYGBge3J9CgpkdF9haXJjb25kaXRpb25pbmdfY291bnRzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoZHRfaG91c2VzJGFpcmNvbmRpdGlvbmluZykpICN0YWJsZSgpIC0gY3JlYXRlcyBmcmVxdWVuY3kgdGFibGUKY29sbmFtZXMoZHRfYWlyY29uZGl0aW9uaW5nX2NvdW50cykgPC0gYygiYWlyY29uZGl0aW9uaW5nX3N0YXR1cyIsICJjb3VudCIpCmR0X2FpcmNvbmRpdGlvbmluZ19jb3VudHMkcGVyY2VudGFnZSA8LSByb3VuZChkdF9haXJjb25kaXRpb25pbmdfY291bnRzJGNvdW50IC8gc3VtKGR0X2FpcmNvbmRpdGlvbmluZ19jb3VudHMkY291bnQpICogMTAwLCAxKQoKZ2dwbG90KGRhdGEgPSBkdF9haXJjb25kaXRpb25pbmdfY291bnRzLCBhZXMoeCA9ICIiLCB5ID0gY291bnQsIGZpbGwgPSBhaXJjb25kaXRpb25pbmdfc3RhdHVzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEsIGNvbG9yID0gIndoaXRlIikgKwogIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQgPSAwKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChwZXJjZW50YWdlLCAiJSIpKSwgCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSA0KSArICAKICB0aGVtZV92b2lkKCkgKyAgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0YxQjE0NyIsICIjNDdCMUYxIikpICsgCiAgbGFicygKICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBBaXJjb25kaXRpb25pbmcgc3RhdHVzIiwKICAgIGZpbGwgPSAiQWlyY29uZGl0aW9uaW5nIFN0YXR1cyIKICApCgpgYGAKCkhlcmUgNjguNCBwZXJjZW50IGhhcyBhaXJjb25kaXRpb25pbmcsIGJ1dCBJIGRvIG5vdCBrbm93LCBob3cgaXQgd2lsbCBhZmZlY3QgcHJlZGljdGlvbnMuCgoKSSB0aGluayB0aGF0IHdvdWxkIGJlIGVub3VnaCBleHBsb3JhdGlvbiBhbmQgd2UgY2FuIHN0YXJ0IHdpdGggb3VyIGZpcnN0IG1vZGVsLgoKIyBNb2RlbHMgMSAmIDIKCkZpcnN0LCBJIHdvdWxkIGxpa2UgdG8gc3RhcnQgcHJldHR5IHNpbXBsZSB3aXRoIGxpbmVhciBtb2RlbC4KCkkgY29uc2lkZXIgdG8gdGFrZSBhbGwgdmFyaWFibGVzIHRvIG15IG1vZGVsLCBiZWNhdXNlIHRoZXkgYWxsIHNlZW0gdG8gYmUgdmVyeSBpbXBvcnRhbnQuCgpCdXQgYmVmb3JlIHdlIHN0YXJ0LCBJIHdhbnQgdG8gaW50cm9kdWNlIGEgZGF0YSB0YWJsZSwgd2hpY2ggd2lsbCBiZSB2ZXJ5IHVzZWZ1bCBpbiB0aGUgZW5kIG9mIHRoaXMgY291cnNlIHdvcmsuCgpgYGB7cn0KZHRfZmVhdHVyZXNfcGVyZm9ybWFuY2UgPC0gZGF0YS50YWJsZSgicHJpY2VfbG1fcm1zZSIgPSBjKDAsIDAsIDAsIDAsIDApLCAicHJpY2VfdHJlZV9ybXNlIiA9IGMoMCwgMCwgMCwgMCwgMCksICJmZWF0dXJlIiA9IGMoMCwgMSwgMiwgMywgNCkpCmBgYAoKCiMjIExpbmVhciBtb2RlbAoKSSB3aWxsIHVzZSBsbSBmdW5jdGlvbiBpbiBSIHRvIGZpbmQgbmVlZGVkIGJldGEgY29lZmZpY2llbnRzIGFuZCBjcmVhdGUgbXkgbW9kZWwKCmBgYHtyfQpwcmljZV9sbSA8LSBsbShmb3JtdWxhID0gcHJpY2UgfiBhcmVhICsgYmVkcm9vbXMgKyBob3R3YXRlcmhlYXRpbmcgKyBhaXJjb25kaXRpb25pbmcgKyBzdG9yaWVzICsgbWFpbnJvYWQgKyBwYXJraW5nICsgZnVybmlzaGluZ3N0YXR1cyArIGJhdGhyb29tcyArIGd1ZXN0cm9vbSArIGJhc2VtZW50ICsgcHJlZmFyZWEsIGRhdGEgPSBkdF9ob3VzZXMpCgpzdW1tYXJ5KHByaWNlX2xtKQpgYGAKCldlIGdvdCAwLjY4IFItc3F1YXJlZCwgd2hpY2ggaXMgbm90IHRoYXQgYmFkIGZvciBhIG1vZGVsIGp1c3QgbWFkZSB1cC4gQnV0IHRoYXQncyBub3QgYWxsLCBJIHdpbGwgdHJ5IHRvIGRvIGJldHRlciBoZXJlLCBidXQgZmlyc3QsIGFub3RoZXIgbW9kZWwuCgpCdXQgSSB3b3VsZCBsaWtlIHRvIG1lYXN1cmUgcGVyZm9ybWFuY2Ugb2YgbXkgbW9kZWxzIHdpdGggUk1TRSwgc28gSSB3aWxsIGNhbGN1bGF0ZSBSTVNFIGZvciBsaW5lYXIgbW9kZWwuCgpgYGB7cn0KcHJpY2VfbG1fcm1zZSA8LSBtZWFuKHNxcnQoYWJzKHByaWNlX2xtJHJlc2lkdWFscykpKQoKcHJpY2VfbG1fcm1zZQpgYGAKCgojIyBUcmVlIE1vZGVsCgpJIHRoaW5rIHRoaXMgbW9kZWwgY291bGQgcGVyZm9ybSBiZXR0ZXIsIGJlY2F1c2UgdGhlcmUgc29tZSB2YXJpYWJsZXMgd2hpY2ggY2FuIGFmZmVjdCB0aGlzIG1vZGVsIG5vdCBvbmx5IGxpbmVhcmx5LCBidXQgdGhlIG90aGVyIHdheSwgaW4gdGhpcyBjYXNlIHRyZWUgbW9kZWwgY2FuIHNob3cgYmV0dGVyIHBlcmZvcm1hbmNlLgoKSW4gdGhpcyBjb3Vyc2V3b3JrIHdpbGwgYmUgdXNlZCBycGFydCB0byBjcmVhdGUgYSByZWdyZXNzaW9uIHRyZWUuCgpgYGB7cn0KcHJpY2VzX3RyZWUgPC0gcnBhcnQoZGF0YSA9IGR0X2hvdXNlcywgZm9ybXVsYSA9IHByaWNlIH4gYXJlYSArIGJlZHJvb21zICsgaG90d2F0ZXJoZWF0aW5nICsgYWlyY29uZGl0aW9uaW5nICsgc3RvcmllcyArIG1haW5yb2FkICsgcGFya2luZyArIGZ1cm5pc2hpbmdzdGF0dXMgKyBiYXRocm9vbXMgKyBndWVzdHJvb20gKyBiYXNlbWVudCArIHByZWZhcmVhLCBtZXRob2QgPSAnYW5vdmEnKQoKcHJwKHByaWNlc190cmVlLCBkaWdpdHMgPSAtMykKYGBgCgpgYGB7cn0KcHJpbnRjcChwcmljZXNfdHJlZSkKYGBgCgpOb3cgYWZ0ZXIgSSBoYXZlIGJ1aWx0IHdpdGggdGhlIGhlbHAgb2YgcnBhcnQgdHJlZSBtb2RlbCBiYXNlZCBvbiBteSBkYXRhc2V0LCBsZXQgdXMgZXhwbG9yZSBpdDoKCmBgYHtyfQpwcmljZXNfdHJlZQpgYGAKCldlIGNhbiBzZWUsIHRoYXQgd2UgaGF2ZSAzMSBOb2RlcywgSSB0aGluayBmb3IgdGhpcyBraW5kIG9mIGRhdGFzZXQgaXQgbWF5IGJlIG9rYXkuCgpOb3cgaXQgd291bGQgYmUgZ3JlYXQgdG8gcHJ1bmUgdGhlIHRyZWUsIGJlY2F1c2UgSSBkbyBub3Qgd2FudCBteSB0cmVlIHRvIG92ZXJmaXQ6CgpgYGB7cn0KcGxvdGNwKHByaWNlc190cmVlKQpgYGAKVGhpcyBpcyBjb21wbGV4aXR5IG9mIHRoaXMgdHJlZS4gV2UgbmVlZCB0aGUgbG93ZXN0IGNvbXBsZXhpdHksIHRvIGdldCBhcyBmZXcgbGVhZnMgYXMgcG9zc2libGUgdG8gZ2V0IHRoZSBiZXN0IHBlcmZvcm1hbmNlLCBzbyB0aGF0IHRyZWUgd29uJ3Qgb3ZlcmZpdCB0aGUgZGF0YS4KCmBgYHtyfQpwcmljZXNfdHJlZV9taW5fY3AgPC0gcHJpY2VzX3RyZWUkY3B0YWJsZVt3aGljaC5taW4ocHJpY2VzX3RyZWUkY3B0YWJsZVssICJ4ZXJyb3IiXSksICJDUCJdCm1vZGVsX3RyZWUgPC0gcHJ1bmUocHJpY2VzX3RyZWUsIGNwID0gcHJpY2VzX3RyZWVfbWluX2NwICkKcHJwKHByaWNlc190cmVlLGRpZ2l0cyA9IC0zKQpgYGAKCmFmdGVyIHdlIHBydW5lZCB0aGUgdHJlZSwgbGV0J3MgY2FsY3VsYXRlIHRoZSBSTVNFIGZvciB0aGUgdHJlZSBtb2RlbAoKCmBgYHtyfQpwcmljZXNfdHJlZV9wcmVkIDwtIHByZWRpY3QocHJpY2VzX3RyZWUsIGR0X2hvdXNlc1ssIGMoImFyZWEiLCJiYXRocm9vbXMiLCAiYmVkcm9vbXMiLCAiaG90d2F0ZXJoZWF0aW5nIiwgImFpcmNvbmRpdGlvbmluZyIsICJwYXJraW5nIiwgInN0b3JpZXMiLCAibWFpbnJvYWQiLCAiZnVybmlzaGluZ3N0YXR1cyIsICJndWVzdHJvb20iLCAiYmFzZW1lbnQiLCAicHJlZmFyZWEiKV0pCnByaWNlc190cmVlX3Jtc2UgPC0gbWVhbihzcXJ0KGFicyhkdF9ob3VzZXMkcHJpY2UgLSBwcmljZXNfdHJlZV9wcmVkKSkpCgpwcmljZXNfdHJlZV9ybXNlCmBgYAoKCiMjIENvbXBhcmluZyB0d28gbW9kZWxzCgpwcmljZSBsaW5lYXIgbW9kZWwgaGFzIGEgUk1TRSBvZiAKCmBgYHtyfQpwcmljZV9sbV9ybXNlCmBgYAoKcHJpY2UgdHJlZSBtb2RlbCBoYXMgYSBSTVNFIG9mIAoKYGBge3J9CnByaWNlc190cmVlX3Jtc2UKYGBgCgoKSXQgaXMgc3VycHJpc2luZyBmb3IgbWUsIGFzIGZvciBhIHBlcnNvbiB3aG8gZG9lcyBub3QgaGF2ZSBhIGxvdCBvZiBleHBlcmllbmNlIGluIG1vZGVsbGluZywgdGhhdCBsaW5lYXIgbW9kZWwgcGVyZm9ybXMgYmV0dGVyIHRoYW4gdHJlZSBtb2RlbCBieSBhcHByb3guIDcuMjglLiAKCmBgYHtyfQoxMDAgLSBwcmljZV9sbV9ybXNlIC8gcHJpY2VzX3RyZWVfcm1zZSAqIDEwMApgYGAKCmNvbGxlY3RpbmcgZGF0YSBmb3IgbXkgc3RhdGlzdGljcyBpbiB0aGUgZW5kCgpgYGB7cn0KZHRfZmVhdHVyZXNfcGVyZm9ybWFuY2UkcHJpY2VfbG1fcm1zZVtkdF9mZWF0dXJlc19wZXJmb3JtYW5jZSRmZWF0dXJlID09IDBdIDwtIHByaWNlX2xtX3Jtc2UKZHRfZmVhdHVyZXNfcGVyZm9ybWFuY2UkcHJpY2VfdHJlZV9ybXNlW2R0X2ZlYXR1cmVzX3BlcmZvcm1hbmNlJGZlYXR1cmUgPT0gMF0gPC0gcHJpY2VzX3RyZWVfcm1zZQpgYGAKCgojIEZlYXR1cmUgRW5naW5lZXJpbmcKCkhlcmUgSSB3b3VsZCBsaWtlIHRvIHRyeSBhbGwgaWRlYXMgYW5kIG9ic2VydmF0aW9ucywgd2hpY2ggSSd2ZSBoYWQgdGhyb3VnaCBteSBjb3Vyc2Ugd29yay4gQXMgSSBoYXZlIGEgbG90IG9mIGJpbmFyeSB2YXJpYWJsZXMgYW5kIHRoZXkgYXJlIGFscmVhZHkgZW5jb2RlZCBieSBSJ3MgbG0gbGlicmFyeSBJIHdvdWxkIHVzZSBmYWN0b3IgdmFyaWFibGVzIHN1Y2ggYXMgYmVkcm9vbXMsIGJhdGhyb29tcyBhbmQgc3Rvcmllcy4KCiMjIEZlYXR1cmUgMQojIyMjIEJlZHJvb21zIGFzIGEgZmFjdG9yIHZhcmlhYmxlCgpJIHdhbnQgdG8gdXNlIGJlZHJvb20gdmFyaWFibGUgYXMgYSBmYWN0b3IsIHRvIGRvIHRoYXQgIEkgd2lsbCBkZWxldGUgb3JpZ2luYWwgYmVkcm9vbXMgYW5kIGFkZCBpbnN0ZWFkIGJlZHJvb21zIGZhY3Rvci4KCiMjIyBMaW5lYXIgTW9kZWwKCkxldCdzIHRyeSBNb2RlbCB3aXRoIGEgbmV3IGZhY3RvciB2YXJpYWJsZS4KCmBgYHtyfQpwcmljZV9sbSA8LSBsbShmb3JtdWxhID0gcHJpY2UgfiBhcmVhICsgZmFjdG9yKGJlZHJvb21zKSArIGhvdHdhdGVyaGVhdGluZyArIGFpcmNvbmRpdGlvbmluZyArIHN0b3JpZXMgKyBtYWlucm9hZCArIHBhcmtpbmcgKyBmdXJuaXNoaW5nc3RhdHVzICsgYmF0aHJvb21zICsgZ3Vlc3Ryb29tICsgYmFzZW1lbnQgKyBwcmVmYXJlYSwgZGF0YSA9IGR0X2hvdXNlcykKCnN1bW1hcnkocHJpY2VfbG0pCmBgYAoKYGBge3J9CnByaWNlX2xtX3Jtc2UgPC0gbWVhbihzcXJ0KGFicyhwcmljZV9sbSRyZXNpZHVhbHMpKSkKCnByaWNlX2xtX3Jtc2UKYGBgCgpOb3cgdGhlIFJNU0UgaXMgYSBsaXR0bGUgYml0IGJldHRlci4gQXBwcm94aW1hdGVseSBieSAwLjEyJS4gTmV4dCBJIGFtIGdvaW5nIHRvIHRlc3QgdGhpcyB2YXJpYWJsZSBvbiB0aGUgdHJlZSBtb2RlbC4KCmBgYHtyfQoxMDAgLSBwcmljZV9sbV9ybXNlIC8gNzk3LjM4MiAqIDEwMApgYGAKCgojIyMgVHJlZSBNb2RlbAoKYGBge3J9CnByaWNlc190cmVlIDwtIHJwYXJ0KGRhdGEgPSBkdF9ob3VzZXMsIGZvcm11bGEgPSBwcmljZSB+IGFyZWEgKyBmYWN0b3IoYmVkcm9vbXMpICsgaG90d2F0ZXJoZWF0aW5nICsgYWlyY29uZGl0aW9uaW5nICsgc3RvcmllcyArIG1haW5yb2FkICsgcGFya2luZyArIGZ1cm5pc2hpbmdzdGF0dXMgKyBiYXRocm9vbXMgKyBndWVzdHJvb20gKyBiYXNlbWVudCArIHByZWZhcmVhLCBtZXRob2QgPSAnYW5vdmEnKQoKcHJwKHByaWNlc190cmVlLCBkaWdpdHMgPSAtMykKYGBgCkkgdGhpbmssIHRoYXQgaW4gZmVhdHVyZSBlbmdpbmllZXJpbmcsIEkgd29uJ3QgcGxvdCBhbnkgdHJlZSBjb21wbGV4aXR5IGFuZCBleHBsb3JlIHRyZWUgaXRzZWxmLCBiZWNhdXNlIGhlcmUgdGhlIG1haW4gZm9jdXMgaXMgb24gdGhlIGJlbmNobWFya2luZyBhbmQgY29tcGFyaW5nIHR3byBtb2RlbHMgd2l0aCBuZXcgZmVhdHVyZXMuIExldCdzIHBydW5lIHRoZSBtb2RlbCBhbmQgbWVhc3VyZSBSTVNFCgpgYGB7cn0KIyBwcnVuaW5nCnByaWNlc190cmVlX21pbl9jcCA8LSBwcmljZXNfdHJlZSRjcHRhYmxlW3doaWNoLm1pbihwcmljZXNfdHJlZSRjcHRhYmxlWywgInhlcnJvciJdKSwgIkNQIl0KbW9kZWxfdHJlZSA8LSBwcnVuZShwcmljZXNfdHJlZSwgY3AgPSBwcmljZXNfdHJlZV9taW5fY3AgKQoKIyBwcmVkaWN0aW5nCnByaWNlc190cmVlX3ByZWQgPC0gcHJlZGljdChwcmljZXNfdHJlZSwgZHRfaG91c2VzWywgYygiYXJlYSIsImJhdGhyb29tcyIsICJiZWRyb29tcyIsICJob3R3YXRlcmhlYXRpbmciLCAiYWlyY29uZGl0aW9uaW5nIiwgInBhcmtpbmciLCAic3RvcmllcyIsICJtYWlucm9hZCIsICJmdXJuaXNoaW5nc3RhdHVzIiwgImd1ZXN0cm9vbSIsICJiYXNlbWVudCIsICJwcmVmYXJlYSIpXSkKCiNjYWxjdWxhdGluZyBSTVNFCnByaWNlc190cmVlX3Jtc2UgPC0gbWVhbihzcXJ0KGFicyhkdF9ob3VzZXMkcHJpY2UgLSBwcmljZXNfdHJlZV9wcmVkKSkpCgpwcmljZXNfdHJlZV9ybXNlCmBgYAoKSXQgcGVyZm9ybXMgdGhlIHNhbWUsIGFuZCBJIHRoaW5rIGl0IHNob3VsZCBiZSBsaWtlIHRoYXQsIGJlY2F1c2UgdHJlZSBpcyBub3Qgc2Vuc2libGUgZm9yIGZhY3RvciB2YXJpYWJsZXMuIEl0IGlzIHN0aWxsIGEgbnVtYmVyLiBJdCBpcyBpbnRlcnByZXRlZCBvdGhlciB3YXkgYnkgbGluZWFyIE1vZGVsLCBidXQgZm9yIHRoZSB0cmVlIGl0IGlzIHRoZSBzYW1lLgoKSGVyZSBJIGFtIGFkZGluZyBteSB2YXJpYWJsZXMgdG8gY3JlYXRlIGEgc21hbGwgY2hhcnQgaW4gdGhlIGVuZC4KYGBge3J9CmR0X2ZlYXR1cmVzX3BlcmZvcm1hbmNlJHByaWNlX2xtX3Jtc2VbZHRfZmVhdHVyZXNfcGVyZm9ybWFuY2UkZmVhdHVyZSA9PSAxXSA8LSBwcmljZV9sbV9ybXNlCmR0X2ZlYXR1cmVzX3BlcmZvcm1hbmNlJHByaWNlX3RyZWVfcm1zZVtkdF9mZWF0dXJlc19wZXJmb3JtYW5jZSRmZWF0dXJlID09IDFdIDwtIHByaWNlc190cmVlX3Jtc2UKYGBgCgojIyMgQ29tcGFyaW5nCgpXaXRoIG5ldyAnZmFjdG9yKGJlZHJvb20pJyBmZWF0dXJlLCBsaW5lYXIgbW9kZWwgcGVyZm9ybXMgYmV0dGVyOiA3OTYuMzk0NyAtIFJNU0UKT24gdGhlIG90aGVyIHNpZGUgdHJlZSBtb2RlbCB3aXRoIG5ldyBmZWF0dXJlIGhhcyBub3QgaW1wcm92ZWQuCgpMaW5lYXIgbW9kZWwgaXMgc3RpbGwgYmV0dGVyLCBidXQgbWF5IGJlIHRoZXJlIGlzIHNvbWUgY2hhbmNlcywgd2UgaGF2ZSAzIG1vcmUgZmVhdHVyZXMuCgoKIyMgRmVhdHVyZSAyCiMjIyMgTW92aW5nIGFyZWEgY2xvc2VyIHRvIEdhdXNzaWFuIChsb2cgdHJhbnNmb3JtYXRpb24pCgp3aGF0IGlmIHdlIHdpbGwgdHJ5IHRvIGJyaW5nIHRoZSBhcmVhIHZhcmlhYmxlIGNsb3NlciB0byBHYXVzc2lhbiB3aXRoIGxvZyB0cmFuc2Zvcm1hdGlvbiwgYmVjYXVzZSBhcmVhIGRlbnNpdHkgaXMgc2tld2VkIHRvIHRoZSBsZWZ0LCBsb2cgdHJhbnNmb3JtYXRpb24gY2FuIGhlbHAgdXMgdG8gbm9ybWFsaXplIHRoZSB2YXJpYWJsZS4KCiMjIyBMaW5lYXIgTW9kZWwKCkkgYW0gZ29pbmcgdG8gY3JlYXRlIGEgbmV3IGNvbHVtbiBpbiBteSBkYXRhIHRhYmxlLCB3aGljaCB3aWxsIGJlIGNhbGxlZCBhcmVhX2xvZyBhbmQgd2lsbCBjb250YWluIGxuKGFyZWFbaV0pLCB3aGVyZSBpIGlzIHRoZSBpbmRleCBvZiB0aGUgcm93LgpgYGB7cn0KZHRfaG91c2VzWywgYXJlYV9sb2cgOj0gbG9nKGFyZWEpXQpgYGAKCgpMZXQncyBhbHNvIHZpc3VhbGl6ZSBpdC4gSGVyZSBmdW5jdGlvbiBsb29rcyBtdWNoIGJhbGFuY2VkIGFuZCBJIHRoaW5rIGl0IHdpbGwgd29yayBiZXR0ZXIuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkdF9ob3VzZXMsIGFlcyh4ID0gYXJlYV9sb2cpKSArIAogIGdlb21fZGVuc2l0eShmaWxsPSIjZjFiMTQ3IiwgY29sb3I9IiNmMWIxNDciLCBhbHBoYT0wLjI1KSArIAogIGxhYnMoCiAgICB4ID0gJ1ByaWNlJywKICAgIHkgPSAnRGVuc2l0eScKICApICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzAwMDAwMCIpKQpgYGAKCk5vdyBJIHdhbnQgdG8gcnVuIHRoZSBtb2RlbCB3aXRoIGEgc2Vjb25kIG5ldyBmZWF0dXJlLgoKYGBge3J9CnByaWNlX2xtIDwtIGxtKGZvcm11bGEgPSBwcmljZSB+IGFyZWEgKyBmYWN0b3IoYmVkcm9vbXMpICsgaG90d2F0ZXJoZWF0aW5nICsgYWlyY29uZGl0aW9uaW5nICsgc3RvcmllcyArIG1haW5yb2FkICsgcGFya2luZyArIGZ1cm5pc2hpbmdzdGF0dXMgKyBiYXRocm9vbXMgKyBndWVzdHJvb20gKyBiYXNlbWVudCArIHByZWZhcmVhICsgYXJlYV9sb2csIGRhdGEgPSBkdF9ob3VzZXMpCgpzdW1tYXJ5KHByaWNlX2xtKQpwcmljZV9sbV9ybXNlIDwtIG1lYW4oc3FydChhYnMocHJpY2VfbG0kcmVzaWR1YWxzKSkpCmBgYAoKYGBge3J9CnByaWNlX2xtX3Jtc2UKYGBgCgoKU3VjY2VzcyEgSXQgbWFrZXMgbGVzcyBlcnJvcnMuIFByZXZpb3VzIHdlIGhhZCBSTVNFIG9mIDc5Ni4zOTQ3LCBub3cgaXQgaXMgNzkyLjMxNjMuIEFsc28gMC41MSUgcGVyZm9ybWFuY2UgaW1wcm92ZW1lbnQuCgpgYGB7cn0KMTAwIC0gcHJpY2VfbG1fcm1zZSAvIDc5Ni4zOTQ3ICogMTAwCmBgYAoKCiMjIyBUcmVlIE1vZGVsCgpJdCBpcyB0cmVlJ3MgdHVybiwgSSB3YW50IHRvIHJ1biBuZXcgZmVhdHVyZSBvbiB0cmVlIE1vZGVsLgoKYGBge3J9CnByaWNlc190cmVlIDwtIHJwYXJ0KGRhdGEgPSBkdF9ob3VzZXMsIGZvcm11bGEgPSBwcmljZSB+IGFyZWEgKyBmYWN0b3IoYmVkcm9vbXMpICsgaG90d2F0ZXJoZWF0aW5nICsgYWlyY29uZGl0aW9uaW5nICsgc3RvcmllcyArIG1haW5yb2FkICsgcGFya2luZyArIGZ1cm5pc2hpbmdzdGF0dXMgKyBiYXRocm9vbXMgKyBndWVzdHJvb20gKyBiYXNlbWVudCArIHByZWZhcmVhICArIGFyZWFfbG9nLCBtZXRob2QgPSAnYW5vdmEnKQoKcHJwKHByaWNlc190cmVlLCBkaWdpdHMgPSAtMykKYGBgCk5vdyBJIHdpbGwgcHJ1bmUgdGhlIHRyZWUgYW5kIHRoZW4gY2FsY3VsYXRlIHRoZSBSTVNFIGZvciB0aGUgbW9kZWwgd2l0aCB0aGlzIG5ldyBmZWF0dXJlLgoKYGBge3J9CnByaWNlc190cmVlX21pbl9jcCA8LSBwcmljZXNfdHJlZSRjcHRhYmxlW3doaWNoLm1pbihwcmljZXNfdHJlZSRjcHRhYmxlWywgInhlcnJvciJdKSwgIkNQIl0KbW9kZWxfdHJlZSA8LSBwcnVuZShwcmljZXNfdHJlZSwgY3AgPSBwcmljZXNfdHJlZV9taW5fY3AgKQpwcnAocHJpY2VzX3RyZWUsZGlnaXRzID0gLTMpCmBgYAoKYW5kIGNhbGN1bGF0aW5nIHRoZSBlcnJvcgoKYGBge3J9CnByaWNlc190cmVlX3ByZWQgPC0gcHJlZGljdChwcmljZXNfdHJlZSwgZHRfaG91c2VzWywgYygiYXJlYSIsImJhdGhyb29tcyIsICJiZWRyb29tcyIsICJob3R3YXRlcmhlYXRpbmciLCAiYWlyY29uZGl0aW9uaW5nIiwgInBhcmtpbmciLCAic3RvcmllcyIsICJtYWlucm9hZCIsICJmdXJuaXNoaW5nc3RhdHVzIiwgImd1ZXN0cm9vbSIsICJiYXNlbWVudCIsICJwcmVmYXJlYSIsICJhcmVhX2xvZyIpXSkKcHJpY2VzX3RyZWVfcm1zZSA8LSBtZWFuKHNxcnQoYWJzKGR0X2hvdXNlcyRwcmljZSAtIHByaWNlc190cmVlX3ByZWQpKSkKCnByaWNlc190cmVlX3Jtc2UKYGBgCgoKWWVwLCB0aGVyZSBpcyBubyBnYWluIGluIHBlcmZvcm1hbmNlLCBhbmQgSSBjb3VsZCBwcm9iYWJseSBzYXkgd2h5LiBMaW5lYXIgbW9kZWwgZ2FpbnMgcGVyZm9ybWFuY2Ugd2hlbiB3ZSBub3JtYWxpemUgdmFyaWFibGVzLCBiZWNhdXNlIHRoaXMgYWxnb3JpdGhtIGlzIHNlbnNpdGl2ZSB0byBHYXVzc2lhbiwgYnV0IHRoZSB0cmVlIG1vZGVsLCBkb2VzIG5vdCAiY2FyZSIgc28gbXVjaCBhYm91dCBkZW5zaXR5IG9mIHRoZSB2YXJpYWJsZXMsIGJlY2F1c2UgaXQgZG9lcyBub3QgY2FsY3VsYXRlICJkaXN0YW5jZSIgYmV0d2VlbiBwb2ludHMgYW5kIGl0IGlzIGdyZWF0IGluIHdvcmtpbmcgd2l0aCBub24tbGluYXIgZGVwZW5kZW5jaWVzLiBUaGlzIGlzIG15IHByZWRpY3Rpb24sIGJ1dCBJIGNvdWxkIGJlIGFsc28gd3JvbmcuCgojIyMgQ29tcGFyaW5nCgpTbyB0aGVyZSBhcmUgUk1TRSBmcm9tIGxpbmVhciBtb2RlbDogNzkyLjMxNjMgYW5kIFJNU0UgZnJvbSB0cmVlOiA4NTAuNTYxLiBMaW5lYXIgbW9kZWwncyBwZXJmb3JtYW5jZSBpcyBiZXR0ZXIgdGhhbiB0cmVlJ3MKCmBgYHtyfQpkdF9mZWF0dXJlc19wZXJmb3JtYW5jZSRwcmljZV9sbV9ybXNlW2R0X2ZlYXR1cmVzX3BlcmZvcm1hbmNlJGZlYXR1cmUgPT0gMl0gPC0gcHJpY2VfbG1fcm1zZQpkdF9mZWF0dXJlc19wZXJmb3JtYW5jZSRwcmljZV90cmVlX3Jtc2VbZHRfZmVhdHVyZXNfcGVyZm9ybWFuY2UkZmVhdHVyZSA9PSAyXSA8LSBwcmljZXNfdHJlZV9ybXNlCmBgYAoKIyMjIFJlbGV2YW50IHN0YXRpc3RpY3MgdGhlb3J5CgpJIHRoaW5rLCB0aGlzIGNvdWxkIGJlIGEgZ29vZCBJZGVhIHRvIHRha2UgYSBsb290IGF0IGEgY29ycmVsYXRpb24gYmV0d2VlbiB2YXJpYWJsZXMsIGJ1dCBmcm9tIERhdGEgZXhwbG9yYXRpb24gSSBjYW4gYWxyZWFkeSBzYXksIHRoYXQgYXJlYSBjb3JyZWxhdGVzIHdpdGggcHJpY2UuCgpIZXJlIHdlIGFyZSwgY29ycmVsYXRpb24gcGxvdDoKCmBgYHtyfQpnZ2NvcnJwbG90KGNvcnIgPSBjb3IoZHRfaG91c2VzWywgLihwcmljZSwgYXJlYSwgYmVkcm9vbXMsIGJhdGhyb29tcywgc3RvcmllcywgcGFya2luZyldKSwgCiAgICAgICAgICAgaGMub3JkZXIgPSBUUlVFLAogICAgICAgICAgIGxhYiA9IFRSVUUpCmBgYAoKSG0sIGNvcnJlbGF0aW9uIHBsb3QgZG9lcyBub3QgbG9vayBhcyBncmVhdCwgYXMgSSBoYXZlIGV4cGVjdGVkLCBidXQgdGhlIHN0cm9uZ2VzdCBjb3JyZWxhdGlvbiB3aXRoIHByaWNlIGlzIGFyZWEgYW5kIGFtb3VudCBvZiBiYXRocm9vbXMuIAoKIyMgRmVhdHVyZSAzCiMjIyMjIFRyZWF0IGJhdGhyb29tcyBhcyBhIGZhY3RvciB2YXJpYWJsZQoKSSBnb3QgYW4gSWRlYSwgd2UgaGF2ZSBiYXRocm9vbXMsIGFuZCB0aGV5IGFyZSBpbiByYW5nZSBmcm9tIDEgdG8gNC5XaGF0IGlmIHdlIHdpbGwgdHJlYXQgZWFjaCBhbW91bnQgb2YgYmF0aHJvb21zIGFzIGEgZmFjdG9yIHZhcmlhYmxlLiBCZWNhdXNlIGl0IGlzIHBvc3NpYmxlIHRoYXQgaG9tZSB3aXRoIDIgYmF0aHJvb21zIGlzIGRyYXN0aWNhbGx5IG1vcmUgZXhwZW5zaXZlIHRoYW4gYSBob3VzZSB3aXRoIDEsIGFuZCB0aGUgb25lIHdpdGggMyBiYXRocm9vbXMgaXMgc3VwZXIgY29zdGx5CgojIyMgTGluZWFyIE1vZGVsCgoKYGBge3J9CnByaWNlX2xtIDwtIGxtKGZvcm11bGEgPSBwcmljZSB+IGFyZWEgKyBmYWN0b3IoYmVkcm9vbXMpICsgaG90d2F0ZXJoZWF0aW5nICsgYWlyY29uZGl0aW9uaW5nICsgc3RvcmllcyArIG1haW5yb2FkICsgcGFya2luZyArIGZ1cm5pc2hpbmdzdGF0dXMgKyBmYWN0b3IoYmF0aHJvb21zKSArIGd1ZXN0cm9vbSArIGJhc2VtZW50ICsgcHJlZmFyZWEgKyBhcmVhX2xvZywgZGF0YSA9IGR0X2hvdXNlcykKCnN1bW1hcnkocHJpY2VfbG0pCnByaWNlX2xtX3Jtc2UgPC0gbWVhbihzcXJ0KGFicyhwcmljZV9sbSRyZXNpZHVhbHMpKSkKYGBgCgpgYGB7cn0KcHJpY2VfbG1fcm1zZQpgYGAKCkFuZCB3ZSBnYWluIGEgbGl0dGxlIGJpdCBtb3JlIHBlcmZvcm1hbmNlLiBUaGlzIGlzIHJlYWxseSBncmVhdC4gQXBwcm94IDEuMTklIGJldHRlciB0aGFuIHRoZSBmaXJzdCBtb2RlbCB3aXRob3V0IGZlYXR1cmVzLiBCdXQgbm90IGV2ZXJ5IGZhY3RvciBpcyB1c2VkLCBtYXkgYmUgdGhlcmUgaXMgYSBiaWcgZGlmZmVyZW5jZSBiZXR3ZWVuIDEgYW5kIDIgYmF0aHJvb21zLCBUaGF0IG1heSBiZSBiZWNhdXNlIHRoZSBsdWNrIG9mIGRhdGEsIGJlY2F1c2UgSSBoYXZlIGxlc3MgdGhhbiA1IHVuaXRzIHdpdGggMyBvciA0IGJhdGhyb29tcyBvdmVyYWxsIGluIG15IGRhdGFzZXQuCgpgYGB7cn0KMTAwIC0gcHJpY2VfbG1fcm1zZSAvIDc5Ny4zODIgKiAxMDAKYGBgCgojIyMgVHJlZSBNb2RlbAoKTGV0IHVzIHRyeSB0cmVlIG1vZGVsIG5vdy4KCmBgYHtyfQpwcmljZXNfdHJlZSA8LSBycGFydChkYXRhID0gZHRfaG91c2VzLCBmb3JtdWxhID0gcHJpY2UgfiBhcmVhICsgZmFjdG9yKGJlZHJvb21zKSArIGhvdHdhdGVyaGVhdGluZyArIGFpcmNvbmRpdGlvbmluZyArIHN0b3JpZXMgKyBtYWlucm9hZCArIHBhcmtpbmcgKyBmdXJuaXNoaW5nc3RhdHVzICsgZmFjdG9yKGJhdGhyb29tcykgKyBndWVzdHJvb20gKyBiYXNlbWVudCArIHByZWZhcmVhICArIGFyZWFfbG9nLCBtZXRob2QgPSAnYW5vdmEnKQoKcHJwKHByaWNlc190cmVlLCBkaWdpdHMgPSAtMykKYGBgCgpOb3cgd2UgaGF2ZSB0byBwcnVuZSB0aGUgdHJlZSBhbmQgdGhlbiBtYWtlIHByZWRpY3Rpb25zIHdpdGggUk1TRSBjYWxjdWxhdGlvbnMuCgpgYGB7cn0KcHJpY2VzX3RyZWVfbWluX2NwIDwtIHByaWNlc190cmVlJGNwdGFibGVbd2hpY2gubWluKHByaWNlc190cmVlJGNwdGFibGVbLCAieGVycm9yIl0pLCAiQ1AiXQptb2RlbF90cmVlIDwtIHBydW5lKHByaWNlc190cmVlLCBjcCA9IHByaWNlc190cmVlX21pbl9jcCApCgoKcHJpY2VzX3RyZWVfcHJlZCA8LSBwcmVkaWN0KHByaWNlc190cmVlLCBkdF9ob3VzZXNbLCBjKCJhcmVhIiwiYmF0aHJvb21zIiwgImJlZHJvb21zIiwgImhvdHdhdGVyaGVhdGluZyIsICJhaXJjb25kaXRpb25pbmciLCAicGFya2luZyIsICJzdG9yaWVzIiwgIm1haW5yb2FkIiwgImZ1cm5pc2hpbmdzdGF0dXMiLCAiZ3Vlc3Ryb29tIiwgImJhc2VtZW50IiwgInByZWZhcmVhIiwgImFyZWFfbG9nIildKQpwcmljZXNfdHJlZV9ybXNlIDwtIG1lYW4oc3FydChhYnMoZHRfaG91c2VzJHByaWNlIC0gcHJpY2VzX3RyZWVfcHJlZCkpKQoKcHJpY2VzX3RyZWVfcm1zZQpgYGAKClRoaXMgaXMgYXdlc29tZSwgd2UgYXJlIG1ha2luZyB+IDE3Ljg5NTEgbGVzcyBlcnJvcnMsIHRoaXMgaXMgMi4wOCUgbGVzcyBlcnJvcnMuCgpgYGB7cn0KMTAwIC0gcHJpY2VzX3RyZWVfcm1zZSAvIDg2MC4wMjIzICogMTAwCmBgYAoKCiMjIyBDb21wYXJpbmcKClRoaXMgYmVjb21lcyBpbnRlcmVzdGluZy4gV2hpbGUgbGluZWFyIG1vZGVsIGhhcyBpbXByb3ZlZCBieSAxLjE5JSwgdHJlZSBtb2RlbCBtYWRlIGJpZ2dlciBnYWluIGluIHBlcmZvcm1hbmNlOiB+Mi4wOCUuIFRoaXMgaXMgfjIgdGltZXMgbGluZWFyIG1vZGVsIGdhaW5zLgoKYGBge3J9CmR0X2ZlYXR1cmVzX3BlcmZvcm1hbmNlJHByaWNlX2xtX3Jtc2VbZHRfZmVhdHVyZXNfcGVyZm9ybWFuY2UkZmVhdHVyZSA9PSAzXSA8LSBwcmljZV9sbV9ybXNlCmR0X2ZlYXR1cmVzX3BlcmZvcm1hbmNlJHByaWNlX3RyZWVfcm1zZVtkdF9mZWF0dXJlc19wZXJmb3JtYW5jZSRmZWF0dXJlID09IDNdIDwtIHByaWNlc190cmVlX3Jtc2UKYGBgCgoKIyMgRmVhdHVyZSA0CiMjIyMgU3RvcmllcyBhcyBhIGZhY3RvciB2YXJpYWJsZQoKSSB3YW50IHRvIHRyZWF0IG15IHN0b3JpZXMgYWxzbyBhcyBhIGNhdGVnb3JpY2FsIGZhY3RvciB2YXJpYWJsZSBhbmQgdG8gZG8gdGhhdCBJIHdpbGwgYWdhaW4gdXNlIGZhY3RvcigpIGZ1bmN0aW9uIGluIFIuIEkgd2lsbCB0cnkgdGhpcyBmZWF0dXJlIGluIGxpbmVhciBhbmQgdHJlZSBtb2RlbC4KCkZpcnN0LCBJIHdvdWxkIGxpa2UgdG8gc3RhcnQgYXMgdXN1YWwgd2l0aCBsaW5lYXIgbW9kZWwuCgojIyMgTGluZWFyIG1vZGVsCgpgYGB7cn0KIyBjYWxjdWxhdGluZyBhbmQgcnVubmluZyBtb2RlbApwcmljZV9sbSA8LSBsbShmb3JtdWxhID0gcHJpY2UgfiBhcmVhICsgZmFjdG9yKGJlZHJvb21zKSArIGhvdHdhdGVyaGVhdGluZyArIGFpcmNvbmRpdGlvbmluZyArIGZhY3RvcihzdG9yaWVzKSArIG1haW5yb2FkICsgcGFya2luZyArIGZ1cm5pc2hpbmdzdGF0dXMgKyBmYWN0b3IoYmF0aHJvb21zKSArIGd1ZXN0cm9vbSArIGJhc2VtZW50ICsgcHJlZmFyZWEgKyBhcmVhX2xvZywgZGF0YSA9IGR0X2hvdXNlcykKCnN1bW1hcnkocHJpY2VfbG0pCmBgYAoKYGBge3J9CnByaWNlX2xtX3Jtc2UgPC0gbWVhbihzcXJ0KGFicyhwcmljZV9sbSRyZXNpZHVhbHMpKSkKcHJpY2VfbG1fcm1zZQpgYGAKClRoZXJlIGlzIGEgbGl0dGxlIHBlcmZvcm1hbmNlIGdhaW4gaW4gbGluZWFyIG1vZGVsLiBJdCBpcyAwLjM3JS4gTm93IGxldCdzIHRlc3QgdGhpcyBmZWF0dXJlIG9uIFRyZWUuCgpgYGB7cn0KMTAwIC0gcHJpY2VfbG1fcm1zZSAvIDc4Ny44NjkzICogMTAwCmBgYAoKCiMjIyBUcmVlIE1vZGVsCgpBZnRlciBteSBwcmV2aW91cyBleHBlcmllbmNlIHdpdGggYmF0aHJvb21zLCBJIHRoaW5rIGhlcmUgd2UgY291bGQgYWxzbyBzZWUgc29tZSBwZXJmb3JtYW5jZSBnYWluLCBhbmQgSSB0aGluayBpdCB3aWxsIGJlIHByb2JhYmx5IGJldHRlciB0aGFuIGJ5IGxpbmVhciBtb2RlbC4gSSB3YW50IG5vdyB0byBydW4gdGhlIHRyZWUgbW9kZWwgYW5kIHRoZW4gcHJ1bmUgYW5kIHByZWRpY3QsIHRoZW4gbWVhc3VyZSB0aGUgUk1TRToKCmBgYHtyfQpwcmljZXNfdHJlZSA8LSBycGFydChkYXRhID0gZHRfaG91c2VzLCBmb3JtdWxhID0gcHJpY2UgfiBhcmVhICsgZmFjdG9yKGJlZHJvb21zKSArIGhvdHdhdGVyaGVhdGluZyArIGFpcmNvbmRpdGlvbmluZyArIGZhY3RvcihzdG9yaWVzKSArIG1haW5yb2FkICsgcGFya2luZyArIGZ1cm5pc2hpbmdzdGF0dXMgKyBmYWN0b3IoYmF0aHJvb21zKSArIGd1ZXN0cm9vbSArIGJhc2VtZW50ICsgcHJlZmFyZWEgKyBhcmVhX2xvZywgbWV0aG9kID0gJ2Fub3ZhJykKCnBycChwcmljZXNfdHJlZSwgZGlnaXRzID0gLTMpCmBgYAoKTm93IEkgYW0gcHJ1bmluZyBhbmQgcHJlZGljdGluZy4gVGhlbiBjYWxjdWxhdGluZyBSTVNFLCB3aGljaCBJIHdhbnQgdG8gY29tcGFyZSB3aXRoIHRoZSBwcmV2aW91cyByZXN1bHQuCgpgYGB7cn0KIyBwcnVuaW5nIApwcmljZXNfdHJlZV9taW5fY3AgPC0gcHJpY2VzX3RyZWUkY3B0YWJsZVt3aGljaC5taW4ocHJpY2VzX3RyZWUkY3B0YWJsZVssICJ4ZXJyb3IiXSksICJDUCJdCm1vZGVsX3RyZWUgPC0gcHJ1bmUocHJpY2VzX3RyZWUsIGNwID0gcHJpY2VzX3RyZWVfbWluX2NwICkKCiMgcHJlZGljdGluZwpwcmljZXNfdHJlZV9wcmVkIDwtIHByZWRpY3QocHJpY2VzX3RyZWUsIGR0X2hvdXNlc1ssIGMoImFyZWEiLCJiYXRocm9vbXMiLCAiYmVkcm9vbXMiLCAiaG90d2F0ZXJoZWF0aW5nIiwgImFpcmNvbmRpdGlvbmluZyIsICJwYXJraW5nIiwgInN0b3JpZXMiLCAibWFpbnJvYWQiLCAiZnVybmlzaGluZ3N0YXR1cyIsICJndWVzdHJvb20iLCAiYmFzZW1lbnQiLCAicHJlZmFyZWEiLCAiYXJlYV9sb2ciKV0pCgojIGNhbGN1bGF0aW5nIFJNU0UKcHJpY2VzX3RyZWVfcm1zZSA8LSBtZWFuKHNxcnQoYWJzKGR0X2hvdXNlcyRwcmljZSAtIHByaWNlc190cmVlX3ByZWQpKSkKCnByaWNlc190cmVlX3Jtc2UKYGBgCgoKYW5kIGl0IHNlZW1zIHRoYXQsIHRoZXJlIGlzIG5vIGdhaW4gaW4gcGVyZm9ybWFuY2UgaWYgd2UgdXNlIHN0b3JpZXMgYXMgYSBmYWN0b3IgaW4gdGhlIHRyZWUgbW9kZWwuCgojIyMgQ29tcGFyaW5nCgpXaXRoIGZlYXR1cmUgbnVtYmVyIDQsIHdlIGhhdmUgc2VlbiB0aGUgZ2FpbiBpbiBsaW5lYXIgbW9kZWwsIGJ1dCB0aGUgdHJlZSBtb2RlbCwgZGlkIG5vdCBnYWluIGFueSBwZXJmb3JtYW5jZS4gSSB0aGluayB0aGF0IHRoZSBzdG9yaWVzIHdlcmUgbm90IHZlcnkgc3Ryb25nIHZhcmlhYmxlIGluIFRyZWUgbW9kZWwgYW5kIGluIExpbmVhciBtb2RlbCBhbHNvLCBiZWNhdXNlIGluIGNvbXBhcmUgd2l0aCBzdWNoIGEgdmFyaWFibGUgbGlrZSBiYXRocm9vbXMsIHN0b3JpZXMgaGFzIGdhaW5lZCAzIHRpbWVzIGZvciBsaW5lYXIgbW9kZWwgYW5kIG5vdGhpbmcgZm9yIHRyZWUgbW9kZWwuCgpBZnRlciB3ZSBpbmNsdWRlZCBmZWF0dXJlIDQsIGxpbmVhciBtb2RlbCBpcyBzdGlsbCBwZXJmb3JtaW5nIGJldHRlciB0aGFuIHRoZSB0cmVlIG1vZGVsIHdpdGggdGhlIG92ZXJhbGwgcGVyZm9ybWFuY2Ugb2YgNzg0LjkyMyBhbmQgODQyLjEyNzEgcmVzcGVjdGl2ZWx5LgoKYGBge3J9CmR0X2ZlYXR1cmVzX3BlcmZvcm1hbmNlJHByaWNlX2xtX3Jtc2VbZHRfZmVhdHVyZXNfcGVyZm9ybWFuY2UkZmVhdHVyZSA9PSA0XSA8LSBwcmljZV9sbV9ybXNlCmR0X2ZlYXR1cmVzX3BlcmZvcm1hbmNlJHByaWNlX3RyZWVfcm1zZVtkdF9mZWF0dXJlc19wZXJmb3JtYW5jZSRmZWF0dXJlID09IDRdIDwtIHByaWNlc190cmVlX3Jtc2UKYGBgCgpOb3cgd2hlbiBJIGhhdmUgbXkgZGF0YSwgdGhpcyBpcyBteSBjb25jbHVzaW9uIHBsb3Q6CgpgYGB7cn0KZ2dwbG90KCkgKyAKICBnZW9tX3BvaW50KGRhdGEgPSBkdF9mZWF0dXJlc19wZXJmb3JtYW5jZSwgYWVzKHggPSBmZWF0dXJlLCB5ID0gcHJpY2VfbG1fcm1zZSksIAogICAgICAgICAgICAgc2l6ZSA9IDQsIGNvbG9yID0gIiMxZjc3YjQiLCBhbHBoYSA9IDAuOCkgKyAKICBnZW9tX2xpbmUoZGF0YSA9IGR0X2ZlYXR1cmVzX3BlcmZvcm1hbmNlLCBhZXMoeCA9IGZlYXR1cmUsIHkgPSBwcmljZV9sbV9ybXNlKSwgCiAgICAgICAgICAgIGNvbG9yID0gIiMxZjc3YjQiLCBsaW5ld2lkdGggPSAxKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IGR0X2ZlYXR1cmVzX3BlcmZvcm1hbmNlLCBhZXMoeCA9IGZlYXR1cmUsIHkgPSBwcmljZV90cmVlX3Jtc2UpLCAKICAgICAgICAgICAgIHNpemUgPSA0LCBjb2xvciA9ICIjZmY3ZjBlIiwgYWxwaGEgPSAwLjgpICsgCiAgZ2VvbV9saW5lKGRhdGEgPSBkdF9mZWF0dXJlc19wZXJmb3JtYW5jZSwgYWVzKHggPSBmZWF0dXJlLCB5ID0gcHJpY2VfdHJlZV9ybXNlKSwgCiAgICAgICAgICAgIGNvbG9yID0gIiNmZjdmMGUiLCBsaW5ld2lkdGggPSAxKSArIAogIGxhYnModGl0bGUgPSAiUGVyZm9ybWFuY2Ugd2l0aCBBbW91bnQgb2YgRmVhdHVyZXMiLCAKICAgICAgIHggPSAiQW1vdW50IG9mIEZlYXR1cmVzIiwgCiAgICAgICB5ID0gIlBlcmZvcm1hbmNlIChSTVNFKSIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiIzAwMDAwMCIpLAogICAgdGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KQogICkKYGBgCldpdGggdGhpcyBwbG90IEkgd2FudGVkIHRvIHNob3cgeW91IG15IHByb2dyZXNzIGluIGNyZWF0aW5nIGZlYXR1cmVzLiBPbiB0aGUgeC1heGlzIHlvdSBjYW4gc2VlIHRoZSBudW1iZXIgb2YgZmVhdHVyZXMsIGZyb20gMCB0byA0LCBiZWNhdXNlIEkgaGF2ZSBpbmNsdWRlZCB0aGUgcmF3IG1vZGVsIGluIHRoaXMgcGxvdC4gT24gdGhlIHktYXhpcyBpcyB0aGUgUk1TRSBvZiB0aGUgbW9kZWxzLiAKClRoZSBvcmFuZ2UgbGluZSBpcyB0aGUgdHJlZSBtb2RlbCBhbmQgdGhlIGJsdWUgbGluZSBpcyB0aGUgbGluZWFyIG1vZGVsLiAKCldpdGggbW9yZSBmZWF0dXJlcyBvdmVyYWxsLCBib3RoIG1vZGVscyBjb3VsZCBwZXJmb3JtIGJldHRlciwgYnV0IGZvciB0aGlzIGRhdGFzZXQgYW5kIG15IGltcGxlbWVudGF0aW9uLCB0aGUgbGluZWFyIG1vZGVsIHBlcmZvcm1zIGJldHRlci4gV2hlbiBJIHN0YXJ0ZWQsIEkgdGhvdWdodCB0aGUgdHJlZSBtb2RlbCB3b3VsZCBwZXJmb3JtIG11Y2ggYmV0dGVyLiAKCkZpbmFsbHksIEkgd291bGQgbGlrZSB0byBtZW50aW9uIHRoYXQgdGhlIHRyZWUgbW9kZWwgaXMgbG93ZXIgaW4gcGVyZm9ybWFuY2UsIGJ1dCB0aGVyZSB3YXMgYSByZWFsbHkgZ29vZCBib29zdCB3aXRoIGZlYXR1cmUgbnVtYmVyIDMuIFdoaWxlIGFsbCA0IGZlYXR1cmVzIGhhdmUgaW1wcm92ZWQgdGhlIGxpbmVhciBtb2RlbCBpbmNyZW1lbnRhbGx5IHdpdGggZWFjaCBuZXcgZmVhdHVyZS4gCgoqKioKCgoKCg==